src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig
, '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

src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig
, '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

src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig
, '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

src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig
, '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

src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig
, '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

src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig
, '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

src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig
, '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

src: run same-priority platform tasks in posting order - #65353

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo
Aug 20, 2026
Merged

src: run same-priority platform tasks in posting order#65353
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/platform-task-queue-fifo

Conversation

@codebytere

@codebyterecodebytere commented Aug 17, 2026

Copy link
Copy Markdown
Member

TaskQueue<T> has been a std::priority_queue since #58047 so that worker tasks honor v8::TaskPriority. Its comparator returns false for entry types without a priority member and for equal priorities, and the comment expects that to keep insertion order. A binary heap doesn't: three tasks pushed A, B, C pop as A, C, B, and 64 pushes come back out as 0, 2, 6, 14, 30, 62, ...

Three queues are affected: the per-isolate foreground queue (tasks of one priority no longer run in the order they were posted), the foreground delayed queue, and the worker runner's DelayedTaskScheduler, where it can hang shutdown. That scheduler drains its local queue in one batch, so when V8 posts a delayed worker task shortly before the platform shuts down, the StopTask pushed by Stop() can run before a ScheduleTask that was pushed earlier; the ScheduleTask then starts a timer on the scheduler's loop after all timers were stopped, and Shutdown() sits in uv_thread_join() until the delay expires (8 s when the late task is the memory reducer). It shows up as an intermittent multi-second exit stall in processes that exit soon after doing some work.

This is the other half of the shutdown race #61999 addressed: that change makes PostDelayedTask() return early once Stop() has run (both under the tasks_ lock), so every ScheduleTask that is queued was queued before the StopTask; with posting order restored it also runs before it, and no further flag is needed.

The fix gives every queued item a sequence number and uses it as the tie breaker, so equal-priority and priority-less tasks come out FIFO again while higher priorities still go first; PopAll() returns the tasks in that order instead of handing out the heap, which also removes the const_cast loops at its three call sites.

Tests: new cctest (TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO across PopAll()/Pop() for a priority-less queue, priority-then-FIFO for TaskQueueEntry) fails on main and passes here; cctest and the default suite pass.

Refs: #58047
Refs: #61999


Disclosure: the code, test and this description were written by Claude Code, directed and reviewed by @codebytere.

@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. c++ Issues and PRs that require attention from people who are familiar with C++. labels Aug 17, 2026
@codecov

codecovBot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment threadsrc/node_platform.cc Outdated
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 17, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@codebytere
codebytereforce-pushed the fix/platform-task-queue-fifo branch from 121482b to 25b1017CompareAugust 18, 2026 08:12
@codebyterecodebytere added request-ci Add this label to start a Jenkins CI on a PR. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Aug 18, 2026
@codebytere
codebytere requested review from aduh95 and anonrig and removed request for joyeecheung and santigimenoAugust 19, 2026 08:53
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 19, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@XinGOfCloude18

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.12%. Comparing base (30bff4a) to head (25b1017).
⚠️ Report is 51 commits behind head on main.

Files with missing linesPatch %Lines
src/node_platform.cc94.73%0 Missing and 1 partial ⚠️
src/node_platform.h80.00%0 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65353 +/- ##
==========================================
- Coverage 90.13% 90.12% -0.02% 
==========================================
Files 752 752 Lines 251568 251857 +289 Branches 47270 47364 +94 ==========================================
+ Hits 226759 226991 +232 - Misses 16168 16191 +23 - Partials 8641 8675 +34 
Files with missing linesCoverage Δ
src/node_platform.cc74.75% <94.73%> (-1.25%)⬇️
src/node_platform.h85.71% <80.00%> (-5.96%)⬇️

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codebyterecodebytere added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@nodejs-github-bot
nodejs-github-bot merged commit ea4b37f into nodejs:mainAug 20, 2026
74 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in ea4b37f

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 20, 2026
@ShenHongFei

ShenHongFei commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

When there is a high degree of concurrency, this modification to the pr might cause node.js to freeze. For example, when a large number of https requests are initiated concurrently. After rolling back this pr, the problem disappeared. I'm not sure about the exact reason.

@ShenHongFei

Copy link
Copy Markdown
Contributor

Reproducible script

constCONCURRENCY=64constURL='https://qq.com'asyncfunctionmain(){console.log(`🚀 Firing ${CONCURRENCY} concurrent requests → ${URL}\n`)constpromises=Array.from({length: CONCURRENCY},async(_,i)=>{conststart=Date.now()try{constres=awaitfetch(URL,{method: 'GET'})awaitres.text()console.log(`#${i+1} ✓ status=${res.status}${Date.now()-start}ms`)return{ok: true,status: res.status}}catch(err){console.log(`#${i+1} ✗ error: ${err.message}`)return{ok: false}}})constresults=awaitPromise.all(promises)constok=results.filter(r=>r.ok).lengthconsole.log(`\n✅ Done: ${ok}/${CONCURRENCY} succeeded`)}main()

@ShenHongFei

Copy link
Copy Markdown
Contributor
image

aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Aug 25, 2026
TaskQueue became a std::priority_queue when worker tasks started to
honor v8::TaskPriority. Its comparator returns false for entry types
without a priority member, and for entries of equal priority, on the
assumption that the heap then keeps insertion order. It does not: three
tasks pushed A, B, C pop as A, C, B, and larger batches come out in
heap order. That affects the per-isolate foreground task queue (tasks
of one priority no longer run in the order they were posted), the
foreground delayed task queue, and the delayed task scheduler of the
worker thread task runner, whose local queue is drained in one batch:
when v8 posts a delayed worker task shortly before the platform shuts
down, the StopTask pushed by Stop() can run before a ScheduleTask that
was pushed earlier, that ScheduleTask then starts a timer on the
scheduler's loop after all timers were supposed to be stopped, and
Shutdown() blocks in uv_thread_join() until the delay (e.g. the 8 s of
the memory reducer) expires.
Give every queued item a sequence number and use it as the tie
breaker, so that tasks of equal priority, and tasks without one, come
out in FIFO order again; higher priorities still come first. PopAll()
now returns the tasks in that order instead of handing out the heap.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65353
Refs: #58047
Refs: #61999
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
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++.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@codebytere@nodejs-github-bot@XinGOfCloude18@ShenHongFei@jasnell@anonrig