sebhildebrandt / systeminformation

System Information Library for Node.JS
MIT License
2.65k stars 300 forks source link

Awaiting promises returned by systeminformation can still block running thread #873

Open sylt opened 7 months ago

sylt commented 7 months ago

Describe the bug Despite that all calls to the library return promises, the promises themselves are blocking, defeating much of the purpose of having them in the first place.

The reason they are blocking is because the implementation of systeminformation uses calls like execSync(), instead of just exec() (and then awaiting the result).

To Reproduce Use-case: Fetching WiFi info running in the electron main process. Steps to reproduce the behavior:

  1. Issue await si.wifiNetworks(), assuming it will take long time (several seconds).
  2. While waiting for the info, the thread awaiting the network info is blocked from doing other work (even though promises are used).

Expected behavior I expect that awaiting the result shouldn't be really blocking the thread, just making it available for other tasks to run while awaiting the result.

Environment (please complete the following information)

Additional information To always reproduce the problem, simply modify an existing command to something like execSync('sleep 10;' + originalCmd).

Workaround Put the offending systeminformation call in a worker thread, which can be properly awaited without blocking anything.

KristjanESPERANTO commented 5 months ago

@sylt You probably mean si.wifiNetworks() instead of si.getWifiNetworks(), right?

I can confirm this issue with si.wifiNetworks(), si.system() and si.osInfo(), but only with an electron app on arm64.

A simple node script on arm64 works. An electron app on x64 works too.

sylt commented 5 months ago

@KristjanESPERANTO: Yes, of course, thank you :) I have corrected the description!

And just to show-case the difference between using execSync and exec, here's an example program when starting a 100 ms timer, but using different strategies depending on if useSync is true or false:

const child_process = require("child_process")
const process = require("process")

const now = new Date();
setTimeout(() => {console.log(new Date() - now + " ms elapsed")}, 100)

const useSync = true;

process.nextTick(async () => {
    if (useSync) {
        child_process.execSync("sleep 2");
    }
    else {
        await child_process.exec("sleep 2");
    }    
})

As can be seen, the timer is prevented from running if we're issuing execSync(), but is allowed to run when calling await exec().