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

process: flaky behavior of 'exit' event handler #12322

Closed
vsemozhetbyt opened this issue Apr 11, 2017 · 16 comments
Closed

process: flaky behavior of 'exit' event handler #12322

vsemozhetbyt opened this issue Apr 11, 2017 · 16 comments
Labels
confirmed-bug Issues with confirmed bugs. process Issues and PRs related to the process subsystem.

Comments

@vsemozhetbyt
Copy link
Contributor

vsemozhetbyt commented Apr 11, 2017

  • Version: '8.0.0-rc.0'
  • Platform: Windows 7 x64
  • Subsystem: process

process.md states:

Listener functions must only perform synchronous operations. The Node.js
process will exit immediately after calling the 'exit' event listeners
causing any additional work still queued in the event loop to be abandoned.
In the following example, for instance, the timeout will never occur:

process.on('exit', (code) => {
  setTimeout(() => {
    console.log('This will not run');
  }, 0);
});

However, the behavior of the code is flaky:

> test.js

> test.js
This will not run

> test.js
This will not run

> test.js

> test.js

> test.js

> test.js
This will not run

...

If this flakiness is not a bug, what would be better?

  1. Increase the timeout up to 1000 or something and save the categorical 'the timeout will never occur'.
  2. State flakiness. If so, what would be preferable wordings for the description and the logged string?
@vsemozhetbyt vsemozhetbyt added doc Issues and PRs related to the documentations. process Issues and PRs related to the process subsystem. labels Apr 11, 2017
@sam-github
Copy link
Contributor

What about a timeout of 1, is that flaky?

@vsemozhetbyt
Copy link
Contributor Author

@sam-github For me, yes, it is.

@drifkin
Copy link
Contributor

drifkin commented Apr 11, 2017

I think a timeout of 0 is the same as 1 due to this code.

fwiw, I prefer (2), since (1) might make people think that there would be no flakiness, even if the timeout were lowered.

@addaleax
Copy link
Member

I’m not sure, but isn’t it a bug that that code actually runs?

@sam-github
Copy link
Contributor

Smells like a bug to me.

@vsemozhetbyt
Copy link
Contributor Author

vsemozhetbyt commented Apr 11, 2017

@sam-github @addaleax Could we check this for various OS?

I've checked for various Node.js versions in Windows 7 x64:

Node.js 4.8.2 x64 (v8 4.5.103.46)
consistently not run

Node.js 6.10.2 x64 (v8 5.1.281.98)
consistently not run

Node.js 7.8.0 x64 (v8 5.5.372.43)
flaky

Node.js 8.0.0-nightly201703249ff7ed23cd x64 (v8 5.6.326.57)
flaky

Node.js 8.0.0-nightly20170404394b6ac5cb x64 (v8 5.7.492.69)
flaky

Node.js 8.0.0-pre x64 (v8 5.8.202)
flaky

Node.js 8.0.0-pre x64 (v8 5.9.0 candidate turbo on)
flaky

Node.js 8.0.0-pre x64 (v8 5.9.0 candidate turbo off)
flaky

@addaleax
Copy link
Member

I can confirm, the behaviour differs between Node 6 and Node 7 on x64 Linux, too.

@Fishrock123 Fishrock123 added the confirmed-bug Issues with confirmed bugs. label Apr 11, 2017
@Fishrock123
Copy link
Contributor

Fishrock123 commented Apr 11, 2017

Does this only happen if you let the process expire normally, or also if you call process.exit()?

If it does not happen with process.exit(), it is simply a race condition with the beforeExit handler, I think:

node/src/node.cc

Lines 4383 to 4392 in affe0f2

if (more == false) {
v8_platform.PumpMessageLoop(isolate);
EmitBeforeExit(&env);
// Emit `beforeExit` if the loop became alive either after emitting
// event, or after running some callbacks.
more = uv_loop_alive(env.event_loop());
if (uv_run(env.event_loop(), UV_RUN_NOWAIT) != 0)
more = true;
}

@Fishrock123 Fishrock123 removed the confirmed-bug Issues with confirmed bugs. label Apr 11, 2017
@Fishrock123
Copy link
Contributor

ok I just double-checked and that doesn't make sense because your scheduling it in the exit handler... Smells like a bug but I'm not sure where

@Fishrock123 Fishrock123 added the confirmed-bug Issues with confirmed bugs. label Apr 11, 2017
@vsemozhetbyt vsemozhetbyt changed the title doc: flaky example for 'exit' event process: flaky behavior of 'exit' event handler Apr 11, 2017
@addaleax
Copy link
Member

aac79df is the first bad commit

/cc @bnoordhuis – This really seems to be the first commit where this happens, although I can’t quite make out why that is from looking at the diff…

@addaleax addaleax removed the doc Issues and PRs related to the documentations. label Apr 11, 2017
@bnoordhuis
Copy link
Member

It's caused by the while (handle_cleanup_waiting_ != 0) uv_run(event_loop(), UV_RUN_ONCE) loop in the Environment destructor.

aac79df merges CleanupHandles() into the destructor, before that commit it was only used in debug-agent.cc.

@addaleax
Copy link
Member

@bnoordhuis Thanks for explaining, makes sense. I would say this is a bug that we should fix by ensuring the “old” behaviour; do you agree? And if you do, would you prefer to do that yourself? I should be able to take care of it, too.

@vsemozhetbyt
Copy link
Contributor Author

Maybe a test could be added to check old behavior.

@addaleax
Copy link
Member

Oh, yeah, definitely – we should have a test for that, no matter whether we change behaviour or not. One can turn your code into a non-flaky version like this:

process.on('exit', (code) => {
  setTimeout(() => {
    console.log('This will not run'); // crash or w/e
  }, 0);
  // busy loop to make sure the timeout always expires during this tick
  const a = process.hrtime();
  while (process.hrtime(a)[1] < 5000);
});

@bnoordhuis
Copy link
Member

It's not as simple as bringing back CleanupHandles() though. See #12344, it breaks #9753.

bnoordhuis added a commit to bnoordhuis/io.js that referenced this issue Apr 19, 2017
It makes timers and other libuv handles fire intermittently after the
'exit' event, contrary to what the documentation states.

Regression introduced in commit aac79df ("src: use stack-allocated
Environment instances") from June last year that made the
`while (handle_cleanup_waiting_ != 0) uv_run(event_loop(), UV_RUN_ONCE)`
loop run unconditionally on exit because it merged CleanupHandles() into
the Environment destructor.

This change breaks parallel/test-async-wrap-throw-from-callback because
the async_wrap idle handle is no longer cleaned up, which I resolved
pragmatically by removing the test.

In all seriousness, it is being removed in the upcoming async_wrap
revamp - it doesn't make sense to sink a lot of time in it now.

Fixes: nodejs#12322
PR-URL: nodejs#12344
Reviewed-By: Colin Ihrig <[email protected]>
@bnoordhuis
Copy link
Member

Fixed in 5ef6000.

danbev pushed a commit to danbev/node that referenced this issue May 2, 2017
It makes timers and other libuv handles fire intermittently after the
'exit' event, contrary to what the documentation states.

Regression introduced in commit aac79df ("src: use stack-allocated
Environment instances") from June last year that made the
`while (handle_cleanup_waiting_ != 0) uv_run(event_loop(), UV_RUN_ONCE)`
loop run unconditionally on exit because it merged CleanupHandles() into
the Environment destructor.

This change breaks parallel/test-async-wrap-throw-from-callback because
the async_wrap idle handle is no longer cleaned up, which I resolved
pragmatically by removing the test.

In all seriousness, it is being removed in the upcoming async_wrap
revamp - it doesn't make sense to sink a lot of time in it now.

Fixes: nodejs#12322
PR-URL: nodejs#12344
Reviewed-By: Colin Ihrig <[email protected]>
ryzokuken added a commit to ryzokuken/node that referenced this issue Mar 29, 2018
Rename test-fs-truncate-nodejsGH-6233 to test-fs-truncate-clear-file-zero
Rename test-process-exit-nodejsGH-12322 to test-process-exit-flaky-handler

Refs: nodejs#19105
Refs: https://github.com/nodejs/node/blob/master/doc/guides/writing-tests.md#test-structure
lpinca pushed a commit that referenced this issue Apr 1, 2018
- Rename test-fs-truncate-GH-6233 to test-fs-truncate-clear-file-zero
- Rename test-process-exit-GH-12322 to test-process-exit-handler

PR-URL: #19668
Refs: #19105
Reviewed-By: Gireesh Punathil <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: James M Snell <[email protected]>
targos pushed a commit that referenced this issue Apr 2, 2018
- Rename test-fs-truncate-GH-6233 to test-fs-truncate-clear-file-zero
- Rename test-process-exit-GH-12322 to test-process-exit-handler

PR-URL: #19668
Refs: #19105
Reviewed-By: Gireesh Punathil <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: James M Snell <[email protected]>
BethGriggs pushed a commit that referenced this issue Dec 4, 2018
- Rename test-fs-truncate-GH-6233 to test-fs-truncate-clear-file-zero
- Rename test-process-exit-GH-12322 to test-process-exit-handler

PR-URL: #19668
Refs: #19105
Reviewed-By: Gireesh Punathil <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug Issues with confirmed bugs. process Issues and PRs related to the process subsystem.
Projects
None yet
Development

No branches or pull requests

6 participants