ohadravid / wmi-rs

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

WMIConnection::create_services spawns 40+ threads #22

Closed JRAndreassen closed 1 year ago

JRAndreassen commented 4 years ago

Hi...

I had something strange happen... the Connection.rs test (and my app) leaves behind 40+ threads after the call to Connection new:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let com_con = COMLibrary::new().unwrap();
        let wmi_con = WMIConnection::new(com_con.into()).unwrap();

        let p_svc = wmi_con.svc();

        assert_eq!(p_svc.is_null(), false);
    }
}

It happens in ConnectServer:

    fn create_services(&mut self, path: &str) -> Result<(), WMIError> {
        debug!("Calling ConnectServer");
        let mut p_svc = ptr::null_mut::<IWbemServices>();
        let object_path_bstr = WideCString::from_str(path)?;
        unsafe {
>           check_hres((*self.loc()).ConnectServer(
                object_path_bstr.as_ptr() as *mut _,
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                0,
                ptr::null_mut(),
                ptr::null_mut(),
                &mut p_svc,
            ))?;
        }
        self.p_svc = NonNull::new(p_svc as *mut IWbemServices);
        debug!("Got service {:?}", self.p_svc);
        Ok(())
    }```

All with same call-stack

NtWaitForAlertByThreadId (@NtWaitForAlertByThreadId:8) RtlSleepConditionVariableSRW (@RtlSleepConditionVariableSRW:80) SleepConditionVariableSRW (@SleepConditionVariableSRW:14) DllCanUnloadNow (@DllCanUnloadNow:10814) DllCanUnloadNow (@DllCanUnloadNow:10814) DllCanUnloadNow (@DllCanUnloadNow:10814) DllCanUnloadNow (@DllCanUnloadNow:5416) DllCanUnloadNow (@DllCanUnloadNow:10814) DllCanUnloadNow (@DllCanUnloadNow:5044) DllCanUnloadNow (@DllCanUnloadNow:10814) BaseThreadInitThunk (@BaseThreadInitThunk:9) RtlUserThreadStart (@RtlUserThreadStart:12)

```text
stable-x86_64-pc-windows-msvc - Up to date : 1.47.0 (18bf6b4f0 2020-10-07)
stable-x86_64-unknown-linux-gnu - Up to date : 1.47.0 (18bf6b4f0 2020-10-07)
wmi v0.6.0 (C:\Development\Rust\rust_cargo_sources\wmi-rs)
├── chrono v0.4.10
│   ├── num-integer v0.1.41
│   │   └── num-traits v0.2.10
│   │       [build-dependencies]
│   │       └── autocfg v0.1.7
│   │   [build-dependencies]
│   │   └── autocfg v0.1.7
│   ├── num-traits v0.2.10 (*)
│   ├── serde v1.0.103
│   │   └── serde_derive v1.0.103
│   │       ├── proc-macro2 v1.0.6
│   │       │   └── unicode-xid v0.2.0
│   │       ├── quote v1.0.2
│   │       │   └── proc-macro2 v1.0.6 (*)
│   │       └── syn v1.0.11
│   │           ├── proc-macro2 v1.0.6 (*)
│   │           ├── quote v1.0.2 (*)
│   │           └── unicode-xid v0.2.0
│   └── time v0.1.42
│       ├── libc v0.2.66
│       └── winapi v0.3.9
├── log v0.4.8
│   └── cfg-if v0.1.10
├── serde v1.0.103 (*)
├── thiserror v1.0.20
│   └── thiserror-impl v1.0.20
│       ├── proc-macro2 v1.0.6 (*)
│       ├── quote v1.0.2 (*)
│       └── syn v1.0.11 (*)
├── widestring v0.4.0
└── winapi v0.3.9
[dev-dependencies]
├── criterion v0.3.0
│   ├── atty v0.2.13
│   │   └── winapi v0.3.9
│   ├── cast v0.2.3
│   │   [build-dependencies]
│   │   └── rustc_version v0.2.3
│   │       └── semver v0.9.0
│   │           └── semver-parser v0.7.0
│   ├── clap v2.33.0
│   │   ├── bitflags v1.2.1
│   │   ├── textwrap v0.11.0
│   │   │   └── unicode-width v0.1.6
│   │   └── unicode-width v0.1.6
│   ├── criterion-plot v0.4.0
│   │   ├── cast v0.2.3 (*)
│   │   └── itertools v0.8.2
│   │       └── either v1.5.3
│   ├── csv v1.1.1
│   │   ├── bstr v0.2.8
│   │   │   ├── lazy_static v1.4.0
│   │   │   ├── memchr v2.2.1
│   │   │   │   └── libc v0.2.66
│   │   │   ├── regex-automata v0.1.8
│   │   │   │   └── byteorder v1.3.2
│   │   │   └── serde v1.0.103 (*)
│   │   ├── csv-core v0.1.6
│   │   │   └── memchr v2.2.1 (*)
│   │   ├── itoa v0.4.4
│   │   ├── ryu v1.0.2
│   │   └── serde v1.0.103 (*)
│   ├── itertools v0.8.2 (*)
│   ├── lazy_static v1.4.0
│   ├── num-traits v0.2.10 (*)
│   ├── rand_core v0.5.1
│   │   └── getrandom v0.1.13
│   │       └── cfg-if v0.1.10
│   ├── rand_os v0.2.2
│   │   ├── getrandom v0.1.13 (*)
│   │   └── rand_core v0.5.1 (*)
│   ├── rand_xoshiro v0.3.1
│   │   └── rand_core v0.5.1 (*)
│   ├── rayon v1.2.1
│   │   ├── crossbeam-deque v0.7.2
│   │   │   ├── crossbeam-epoch v0.8.0
│   │   │   │   ├── cfg-if v0.1.10
│   │   │   │   ├── crossbeam-utils v0.7.0
│   │   │   │   │   ├── cfg-if v0.1.10
│   │   │   │   │   └── lazy_static v1.4.0
│   │   │   │   │   [build-dependencies]
│   │   │   │   │   └── autocfg v0.1.7
│   │   │   │   ├── lazy_static v1.4.0
│   │   │   │   ├── memoffset v0.5.3
│   │   │   │   │   [build-dependencies]
│   │   │   │   │   └── rustc_version v0.2.3 (*)
│   │   │   │   └── scopeguard v1.0.0
│   │   │   │   [build-dependencies]
│   │   │   │   └── autocfg v0.1.7
│   │   │   └── crossbeam-utils v0.7.0 (*)
│   │   ├── either v1.5.3
│   │   └── rayon-core v1.6.1
│   │       ├── crossbeam-deque v0.7.2 (*)
│   │       ├── crossbeam-queue v0.2.0
│   │       │   └── crossbeam-utils v0.7.0 (*)
│   │       ├── crossbeam-utils v0.7.0 (*)
│   │       ├── lazy_static v1.4.0
│   │       └── num_cpus v1.11.1
│   │           └── libc v0.2.66
│   ├── serde v1.0.103 (*)
│   ├── serde_derive v1.0.103 (*)
│   ├── serde_json v1.0.42
│   │   ├── itoa v0.4.4
│   │   ├── ryu v1.0.2
│   │   └── serde v1.0.103 (*)
│   ├── tinytemplate v1.0.2
│   │   ├── serde v1.0.103 (*)
│   │   └── serde_json v1.0.42 (*)
│   └── walkdir v2.2.9
│       ├── same-file v1.0.5
│       │   └── winapi-util v0.1.2
│       │       └── winapi v0.3.9
│       ├── winapi v0.3.9
│       └── winapi-util v0.1.2 (*)
├── lazy_static v1.4.0
└── serde_json v1.0.42 (*)

Any ideas ?

Thanks JR

ohadravid commented 4 years ago

Hi @JRAndreassen!

I'm not sure I understand your setup 🤔 Are you running cargo test in wmi-rs? Do your app's tests get stuck and never finish?

Note that cargo test uses multithreading by default to run tests in parallel, which might also explain what you are seeing.

For me running locally I see the expected number of threads (1 for the app, 1 from COM) for:

A simple app which creates a connection and waits:

use std::io::{self, Read};
use wmi::{COMLibrary, WMIConnection};

fn main() {
    let com_con = COMLibrary::new().unwrap();
    let wmi_con = WMIConnection::new(com_con.into()).unwrap();

    let p_svc = wmi_con.svc();

    assert_eq!(p_svc.is_null(), false);

    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();
}

And a simple test (with this code after the code from above):

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let com_con = COMLibrary::new().unwrap();
        let wmi_con = WMIConnection::new(com_con.into()).unwrap();

        let p_svc = wmi_con.svc();

        assert_eq!(p_svc.is_null(), false);

        let mut buffer = String::new();
        io::stdin().read_line(&mut buffer).unwrap();
    }
}
JRAndreassen commented 4 years ago

@ohadravid : Thanks for getting back to me...

The app runs just fine, it is a resource consumption issue... When I run the test (or my app), the the call to "WMIConnection::new(com_con.into())" leaves behind 40+ threads (who appear "dead" or may be threads blocked on a wait condition waiting unload the Dll)... Given the stack:

NtWaitForAlertByThreadId (@NtWaitForAlertByThreadId:8) RtlSleepConditionVariableSRW (@RtlSleepConditionVariableSRW:80) SleepConditionVariableSRW (@SleepConditionVariableSRW:14) DllCanUnloadNow (@DllCanUnloadNow:10814)

I have not seen that in any other Windows rust crates, like "winreg" ... I'm just trying to find out what's causing them and if there is a way to get around it... 40+ "dead" threads is a bit excessive... JR

ohadravid commented 4 years ago

Could you try to create a minimal reproduction (a complete crate / a demo repo I could compile)? As I said, in my env with the code above I only see a single thread.

Also can you please provide the method used to list the threads?

JRAndreassen commented 4 years ago

@ohadravid ,

I happens with the test above... Both in VSCode debugger and release code. I'm using the LLDB Debugger and the task list... I'll try to make a example... JR

JRAndreassen commented 4 years ago

@ohadravid ...

One thing that might help is if you send me your "Cargo.lock"... Then we can see if there is a library difference...

ohadravid commented 1 year ago

Closing for now, feel free to reopen if you can create a repro 🙏