Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platform thread race: process.exit executed before background threads are ready #23065

Closed
ofrobots opened this issue Sep 24, 2018 · 7 comments
Closed
Labels
blocked PRs that are blocked by other issues or PRs. v8 platform Issues and PRs related to Node's v8::Platform implementation.

Comments

@ofrobots
Copy link
Contributor

ofrobots commented Sep 24, 2018

Forked issue from #22938 (comment).

This test case:

const assert = require('assert');
const child_process = require('child_process');
const { promisify } = require('util');
const execFile = promisify(child_process.execFile);

const code =
  'console.log(42);process.exit(1)';

{
  execFile(process.execPath, ['-e', code])
    .catch((err) => {
      console.log(`child stderr: >>>\n${err.stderr}<<<`);
      assert.strictEqual(err.code, 1);
      assert.strictEqual(err.stdout, '42\n');
    });
}

Intermittently crashes in the child process on windows when a small IO delay is introduced in the platform worker thread startup:

static void PlatformWorkerThread(void* data) {
  fprintf(stderr, ""); // write an empty string to stderr, just to introduce a delay.
  TRACE_EVENT_METADATA1("__metadata", "thread_name", "name",
                        "PlatformWorkerThread");
  TaskQueue<Task>* pending_worker_tasks = static_cast<TaskQueue<Task>*>(data);
  while (std::unique_ptr<Task> task = pending_worker_tasks->BlockingPop()) {
    task->Run();
    pending_worker_tasks->NotifyOfCompletion();
  }
}

Crash:

C:\workspace\ofrobots\test\common\index.js:662
const crashOnUnhandledRejection = (err) => { throw err; };
                                             ^

AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal:
�[32m+ actual�[39m �[31m- expected�[39m

�[32m+�[39m 3221225477
�[31m-�[39m 1
    at execFile.catch.common.mustCall (C:\workspace\ofrobots\test\parallel\test-child-process-promisified.js:47:14)
    at C:\workspace\ofrobots\test\common\index.js:349:15
    at process._tickCallback (internal/process/next_tick.js:68:7)

The platform worker thread is trying to do IO while the main thread is already shutting things down in exit.

/cc @nodejs/platform-windows

@ofrobots
Copy link
Contributor Author

Okay, it seems that Unix platforms are a lot faster in the time between uv_thread_create is called and the thread loop is actually called. On windows, the main thread calls uv_thread_create and proceed to execute process.exit(1) before the threads have actually started running. The threads do eventually start but crash because the process is in the middle of being teared down.

This happens on windows but seems to be a general problem with how we are doing background threads in Node. process.exit doesn't wait to ensure that process is in a sane state before proceeding (as it rightly should not).

I think the right answer would be for the main thread to wait for the background threads to be initialized before executing user code, but I am interested in other opinions. /cc @addaleax @jasnell @nodejs/libuv.

@ofrobots ofrobots changed the title Platform thread race on windows Platform thread race: process.exit executed before background threads are ready Sep 25, 2018
@jasnell
Copy link
Member

jasnell commented Sep 25, 2018

I think you're right. Initializing the background threads should complete before progressing forward with any bootstrap or post-bootstrap execution of any code that may trigger a process.exit

@addaleax
Copy link
Member

That seems like a good idea, but it sounds like it could affect Windows startup performance significantly?

In the case of libuv, we do wait until all threads in the pool have spawned, and on non-Windows additionally wait for them to uv_thread_join() on exit.

@ofrobots
Copy link
Contributor Author

@addaleax I doubt this will affect startup performance significantly anywhere. I can measure. I think this should be done on all platforms. We have only seen this manifest on windows so far, but there the race exists on all platforms. It would not be safe to give control to any bootstrap code while the background worker threads are still initializing.

@ofrobots
Copy link
Contributor Author

While implementing a fix, I ran into a bug in libuv thread synchronization primitives on mac. This is currently blocked on libuv/libuv#2003.

@ofrobots ofrobots added the blocked PRs that are blocked by other issues or PRs. label Sep 25, 2018
ofrobots added a commit to ofrobots/node that referenced this issue Oct 1, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: nodejs#23065
@ofrobots
Copy link
Contributor Author

ofrobots commented Oct 1, 2018

@addaleax I did some measurements using this benchmark: https://gist.github.com/ofrobots/f686aa7bc33b21e9c09bece53bb8bf52. This runs node -e 0 in a loop 50 times and measures the time taken. 10 such measurements are taken in a single benchmark run. I looked at the mean and the standard deviation on the 10 measurements. I repeated this three times. This experiment was performed on a windows workstation.

Baseline (no thread sync on startup):

Run Average (ms) Stdev (ms)
Run 1 7657.5 196
Run 2 7786.7 74
Run 3 7751.3 104

With a thread sync on startup:

Run Average (ms) Stdev (ms)
Run 1 7722.9 50
Run 2 7737 54
Run 3 7756.1 70

Based on this there is no evidence of noticeable impact on startup. I get similar data on my Mac laptop (interestingly, we are 2x faster starting up on a Mac. This may be something worth chasing down at some point... )

Commits under test:

ofrobots added a commit to ofrobots/node that referenced this issue Oct 3, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: nodejs#23065
Trott pushed a commit to Trott/io.js that referenced this issue Oct 6, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: nodejs#23065

PR-URL: nodejs#23233
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
@Trott
Copy link
Member

Trott commented Oct 6, 2018

Fixed in e273abc

@Trott Trott closed this as completed Oct 6, 2018
ofrobots added a commit to ofrobots/node that referenced this issue Oct 13, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: nodejs#23065

PR-URL: nodejs#23233
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
ofrobots added a commit that referenced this issue Oct 15, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: #23065

PR-URL: #23233
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
jasnell pushed a commit that referenced this issue Oct 17, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: #23065

PR-URL: #23233
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
MylesBorins pushed a commit that referenced this issue Oct 30, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: #23065

PR-URL: #23233
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
rvagg pushed a commit that referenced this issue Nov 28, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: #23065

PR-URL: #23233
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
MylesBorins pushed a commit that referenced this issue Nov 29, 2018
Make sure background workers are ready before proceeding with the
bootstrap or post-bootstrap execution of any code that may trigger
`process.exit()`.

Fixes: #23065

PR-URL: #23233
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
@addaleax addaleax added the v8 platform Issues and PRs related to Node's v8::Platform implementation. label Feb 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked PRs that are blocked by other issues or PRs. v8 platform Issues and PRs related to Node's v8::Platform implementation.
Projects
None yet
4 participants