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

Unreferenced timer started in uncaughtException handler fires immediately if exception was thrown from inside a timer #19970

Closed
novemberborn opened this issue Apr 12, 2018 · 11 comments
Labels
timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout.

Comments

@novemberborn
Copy link

  • Version: v4.8.7, v6.12.3, v8.11.0, v9.11.1
  • Platform: MacOS 10.13.4
  • Subsystem: Timers

I have an uncaughtException handler which performs asynchronous cleanup. This cleanup may only take a certain amount of time, after which the process is forcibly exited. This time limitation is implemented through an unreferenced setTimeout.

If an uncaught exception is thrown from inside another timer, then my cleanup timeout fires instantly.

Here's a reproduction:

'use strict'

process.on('uncaughtException', err => {
  console.error('caught exception', err)

  let start = Date.now()
  const timer = setTimeout(() => {
    console.error('timeout duration %sms', Date.now() - start)
  }, 1000).unref()
  console.error('created timer')
})

setTimeout(() => {
  throw new Error('trigger')
}, 1000)

setTimeout(() => {}, 5000) // keep process alive
$ node test.js

caught exception Error: trigger
    at Timeout.setTimeout [as _onTimeout] (/private/var/folders/2_/qczp184x76b2nl034sq5hvxw0000gn/T/tmp.iAigvol9mj/test.js:12:9)
    at ontimeout (timers.js:466:11)
    at tryOnTimeout (timers.js:304:5)
    at Timer.listOnTimeout (timers.js:267:5)
created timer
timeout duration 2ms

The behavior is as expected when the timer is not unreferenced.

@novemberborn
Copy link
Author

And yes an uncaught exception occurred so it could be argued the timer behavior is undefined. It is really surprising though.

@apapirovski
Copy link
Member

This was fixed at some point recently (works as expected on master) and will be in 10.x but it might've been semver-major. I'll look into which exact commit fixed it.

@apapirovski
Copy link
Member

It might've been #18486 which is indeed semver-major.

@novemberborn
Copy link
Author

Oh that's great @apapirovski!

It's not clear to me from that PR why it's semver-major?

@apapirovski
Copy link
Member

@novemberborn The error handling behaviour is dramatically different now which could break existing code in unexpected ways. I'm not saying it's likely but I think we will want to at least give it some time in that state before we revisit whether it's a good idea to backport and decrease its status from a semver-major change. It's also possible there are new undiscovered bugs so we wouldn't to just land on an LTS release like 8.x.

@apapirovski
Copy link
Member

This of course all relies on me being right re: that being the fix. I'm going to do a bit more testing when I have time to confirm.

@apapirovski apapirovski self-assigned this Apr 12, 2018
@apapirovski apapirovski added the timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout. label Apr 12, 2018
@Fishrock123
Copy link
Contributor

Sounds like a bug to me.

@apapirovski
Copy link
Member

Ok, so it was that PR and the first commit in it that fixed it. I don't think we can backport that fix but I've got a separate one that I'll open a PR for. We should be able to get that into v6.x, v8.x & v9.x, I think.

@apapirovski
Copy link
Member

Fix in #20025

@apapirovski
Copy link
Member

v8.x fix in #20497

MylesBorins pushed a commit that referenced this issue May 4, 2018
When a timeout within a list of timeouts (that consists of only
that specific timeout) throws during its execution, it's possible
for the TimerWrap handle to become shared between both that list
and an unref'd timeout created in the future. This fixes the bug
by extending error handling in timeout execution to check for
whether the current list is empty and if so do cleanup on it.

PR-URL: #20497
Fixes: #19970
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Shingo Inoue <[email protected]>
@apapirovski
Copy link
Member

Closing now that the fix landed in v8.x-staging

@apapirovski apapirovski removed their assignment May 6, 2018
MylesBorins pushed a commit that referenced this issue May 15, 2018
When a timeout within a list of timeouts (that consists of only
that specific timeout) throws during its execution, it's possible
for the TimerWrap handle to become shared between both that list
and an unref'd timeout created in the future. This fixes the bug
by extending error handling in timeout execution to check for
whether the current list is empty and if so do cleanup on it.

PR-URL: #20497
Fixes: #19970
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Shingo Inoue <[email protected]>
MylesBorins pushed a commit that referenced this issue May 15, 2018
When a timeout within a list of timeouts (that consists of only
that specific timeout) throws during its execution, it's possible
for the TimerWrap handle to become shared between both that list
and an unref'd timeout created in the future. This fixes the bug
by extending error handling in timeout execution to check for
whether the current list is empty and if so do cleanup on it.

PR-URL: #20497
Fixes: #19970
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Shingo Inoue <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout.
Projects
None yet
Development

No branches or pull requests

3 participants