Uh oh!
There was an error while loading. Please reload this page.
src: run same-priority platform tasks in posting order - #65353
Conversation
Codecov Report❌ Patch coverage is
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
🚀 New features to boost your workflow:
|
Uh oh!
There was an error while loading. Please reload this page.
nodejs-github-bot
commented
Aug 17, 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>
121482b to
25b1017Comparenodejs-github-bot
commented
Aug 19, 2026
nodejs-github-bot
commented
Aug 19, 2026
XinGOfCloude18
commented
Aug 19, 2026
|
nodejs-github-bot
commented
Aug 20, 2026
Uh oh!
There was an error while loading. Please reload this page.
nodejs-github-bot
commented
Aug 20, 2026
Landed in ea4b37f |
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
commented
Aug 21, 2026
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
commented
Aug 21, 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>
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>

TaskQueue<T>has been astd::priority_queuesince #58047 so that worker tasks honorv8::TaskPriority. Its comparator returnsfalsefor entry types without aprioritymember 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, theStopTaskpushed byStop()can run before aScheduleTaskthat was pushed earlier; theScheduleTaskthen starts a timer on the scheduler's loop after all timers were stopped, andShutdown()sits inuv_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 onceStop()has run (both under thetasks_lock), so everyScheduleTaskthat is queued was queued before theStopTask; 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 theconst_castloops at its three call sites.Tests: new cctest (
TaskQueueTest.HigherPriorityFirstThenPostingOrder: FIFO acrossPopAll()/Pop()for a priority-less queue, priority-then-FIFO forTaskQueueEntry) fails onmainand 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.