ohadravid / wmi-rs

WMI crate for rust
Apache License 2.0
84 stars 27 forks source link

Can't use WMIConnection in background async closures #55

Closed feelingnothing closed 2 years ago

feelingnothing commented 2 years ago

I could be wrong, but the tokio::task::spawn_local nor tokio::spawn didn't work for me. tokio::spawn didn't compile as expected, but tokio::task::spawn_local panicked: thread 'main' panicked at 'spawn_local called from outside of a task::LocalSet'. I tried to spawn a background task that uses wmi.

Edit: Found out previous versions used std::rc::Rc for COMLibrary, so #53 is not direct issue, but not being able to spawn background tasks using wmi is something to consider

ohadravid commented 2 years ago

I agree that spawn/span_local are useful, and I also encountered this during testing.

The main problem is that COM must be initialized for each thread, so the future is "pinned" to the thread in which it was spawned (or rather, where COMLibrary::new was called), which isn't an API tokio exposes directly.

However, tokio_util has a nice solution, which looks like this in our case:

use serde::Deserialize;
use wmi::{COMLibrary, WMIConnection};

#[tokio::main]
async fn main() {
    let pool = tokio_util::task::LocalPoolHandle::new(4);

    pool.spawn_pinned(|| {
        let wmi_con = WMIConnection::new(COMLibrary::new().unwrap().into()).unwrap();

        async move {
            #[derive(Deserialize, Debug)]
            struct Win32_OperatingSystem {
                Name: String,
            }

            let results: Vec<Win32_OperatingSystem> = wmi_con.async_query().await.unwrap();

            for os in results {
                println!("{:#?}", os);
            }
        }
    })
    .await
    .unwrap();
}

Does this fit your usecase?

feelingnothing commented 2 years ago

Perfect! Thanks