promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

promises: refactor rejection handling - #18207

Closed
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor
Closed

promises: refactor rejection handling#18207
apapirovski wants to merge 1 commit into
nodejs:masterfrom
apapirovski:patch-promise-reject-refactor

Conversation

@apapirovski

Copy link
Copy Markdown
Contributor

Remove the unnecessary microTasksTickObject for scheduling microtasks and instead use TickInfo to keep track of whether promise rejections exist that need to be emitted. Consequently allow the microtasks to execute on average fewer times, in more predictable manner than previously.

Simplify unhandled & handled rejection tracking to do more in C++ to avoid needing to expose additional info in JS. Unite emitting unhandledRejection and rejectionHandled into a single function: emitPromiseRejectionWarnings, which runs after all nextTicks have executed.

When new unhandledRejections are emitted within an unhandledRejection handler, allow the event loop to proceed first instead. This means that if the end-user code handles all promise rejections on nextTick, rejections within unhandledRejection now won't spiral into an infinite loop.

On the whole, this should hopefully make reasoning about nextTick, promises and promise rejections a whole lot simpler.

Fixes: #17913

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

process, promises, src

@apapirovskiapapirovski added c++ Issues and PRs that require attention from people who are familiar with C++. process Issues and PRs related to the process subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. promises Issues and PRs related to ECMAScript promises. labels Jan 17, 2018
@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. labels Jan 17, 2018
@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

CitGM failures unrelated to this PR but clearly something landed in the last 24 hours that completely destroyed CitGM.

@benjamingrbenjamingr self-assigned this Jan 17, 2018
@benjamingr

benjamingr commented Jan 17, 2018

Copy link
Copy Markdown
Member

I'll need a few days to digest this and run through the edge cases we had when we specified the hooks.

Pinging (no pressure to participate!) relevant parties @petkaantonov@addaleax@domenic

@apapirovski

apapirovski commented Jan 17, 2018

Copy link
Copy Markdown
ContributorAuthor

Just to distill what's going on in the current loop, here's an outline:

  1. Check if any next ticks exist (C++)
    a. If none exist, run Microtasks
    b. If any ticks exist or unhandled/handled promise rejections were added, go to 2; otherwise exit
  2. Execute all currently scheduled next ticks (if any)
  3. Execute microtasks
  4. Check if any next ticks exist, if they do go to 2.
  5. Emit async handled rejections
  6. Emit all currently existing unhandled promise rejections
    a. if any unhandledRejection listeners exist or there are newly added unhandled promise rejections then go to 2
  7. Next tick loop is done (back we go to C++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure, but, common.mustCall()? ;)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it checks at exit, this would just keep looping forever until timeout is hit.

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the code and testing it - I like the behavior and changes. LGTM.

@benjamingrbenjamingr removed their assignment Jan 18, 2018
@apapirovskiapapirovski added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Jan 18, 2018
@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
@apapirovski
apapirovskiforce-pushed the patch-promise-reject-refactor branch from 0413ebb to 21a2220CompareJanuary 19, 2018 03:55
@BridgeAR

Copy link
Copy Markdown
Member

New CI due to some changed code (If I am not mistaken) https://ci.nodejs.org/job/node-test-pull-request/12619/

@apapirovski

apapirovski commented Jan 19, 2018

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR It was just rebased to make it possible to run the CitGM. But no harm in extra CI :)

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

Landed in d62566e

@apapirovski
apapirovski deleted the patch-promise-reject-refactor branch January 21, 2018 17:38
apapirovski added a commit that referenced this pull request Jan 21, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: #18207Fixes: #17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@MylesBorins

Copy link
Copy Markdown
Contributor

This does not land cleanly on v9.x, should we backport?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@MylesBorins v9.x might be missing some preceding commits, I think. I'll have time to look into it at the end of the week or the weekend, swamped with a move right now. Sorry.

@addaleax

Copy link
Copy Markdown
Member

This seem to apply cleanly on v9.x now, I’m removing the label.

@addaleaxaddaleax removed backport-requested-v9.x author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Feb 27, 2018
addaleax pushed a commit to addaleax/node that referenced this pull request Feb 27, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@addaleaxaddaleax mentioned this pull request Feb 27, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Remove the unnecessary microTasksTickObject for scheduling microtasks
and instead use TickInfo to keep track of whether promise rejections
exist that need to be emitted. Consequently allow the microtasks to
execute on average fewer times, in more predictable manner than
previously.
Simplify unhandled & handled rejection tracking to do more in C++ to
avoid needing to expose additional info in JS.
When new unhandledRejections are emitted within an unhandledRejection
handler, allow the event loop to proceed first instead. This means
that if the end-user code handles all promise rejections on nextTick,
rejections within unhandledRejection now won't spiral into an infinite
loop.
PR-URL: nodejs#18207Fixes: nodejs#17913
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@codebytere

Copy link
Copy Markdown
Member

@apapirovski this doesn't land cleanly on v8.x, do you think this makes sense to backport to v8.x?

@apapirovski

Copy link
Copy Markdown
ContributorAuthor

@codebytere Yeah, probably wouldn't hurt. I have a backlog of things I'm supposed to backport anyway. I'll do them all this weekend.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++Issues and PRs that require attention from people who are familiar with C++.lib / srcIssues and PRs involving general changes in the lib/ or src/ directories.processIssues and PRs related to the process subsystem.promisesIssues and PRs related to ECMAScript promises.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unhandledRejection falls into infinite recursion

7 participants

@apapirovski@benjamingr@BridgeAR@MylesBorins@addaleax@codebytere@nodejs-github-bot