Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, '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

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, '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

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, '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

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, '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

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, '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

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, '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

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, '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

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

V8 JavaScript Engine

V8 is Google's open source JavaScript engine.

V8 implements ECMAScript as specified in ECMA-262.

V8 is written in C++ and is used in Chromium, the open source browser from Google.

V8 can run standalone, or can be embedded into any C++ application.

V8 Project page: https://v8.dev/docs

Getting the Code

Checkout depot tools, and run

 fetch v8

This will checkout V8 into the directory v8 and fetch all of its dependencies. To stay up to date, run

 git pull origin
gclient sync

For fetching all branches, add the following into your remote configuration in .git/config:

 fetch = +refs/branch-heads/*:refs/remotes/branch-heads/*
fetch = +refs/tags/*:refs/tags/*

Multithreading Support

V8 now includes experimental multithreading support that enables true parallel JavaScript execution across multiple OS threads. The design is inspired by Rust's threading model — safe concurrency through ownership and message passing — while staying idiomatic to JavaScript with full async/await integration.

Architecture Overview

  • Work-stealing thread pool: A fixed-size pool of OS threads (defaults to navigator.hardwareConcurrency). Idle threads steal tasks from busy ones for optimal load balancing.
  • Isolate-per-pool-thread: Each pool thread owns a pre-warmed V8 Isolate, keeping memory usage fixed and avoiding per-spawn overhead.
  • Shared-nothing memory model: Threads do not share mutable state. Data moves between threads via structured cloning (deep copy), transferables (move semantics), or SharedArrayBuffer (zero-copy for advanced use).
  • Async-first design: Every blocking operation returns a Promise, so threads integrate naturally with async/await and the event loop.

JavaScript API

For a simplified and fully explained guide of the APIs, see the Multithreading API Documentation.

Thread.spawn — Async Task Execution

Thread.spawn schedules a function on a pool thread and returns an awaitable JoinHandle:

// Basic spawn + awaitconsthandle=Thread.spawn(()=>{returnfibonacci(40);});constresult=awaithandle.join();// 102334155// Spawn with arguments (serialized via structured clone)consthandle=Thread.spawn((a,b)=>{returna*b;},6,7);constresult=awaithandle.join();// 42// Async function inside a threadconsthandle=Thread.spawn(async()=>{constdata=awaitfetchData();// each thread has its own event loopreturnprocessData(data);});constresult=awaithandle.join();

Thread.sleep — Non-blocking Sleep

asyncfunctiondelayedWork(){console.log("Starting...");awaitThread.sleep(1000);// sleeps 1 second, non-blockingconsole.log("Done!");}

Channels — Async Message Passing (like Rust's mpsc)

Channels provide safe cross-thread communication with async/await:

const[tx,rx]=Thread.channel();// Producer threadThread.spawn(async()=>{for(leti=0;i<10;i++){awaittx.send({index: i,value: i*i});awaitThread.sleep(100);}tx.close();// signal no more messages});// Consumer — async iterationforawait(constmsgofrx){console.log(msg);// { index: 0, value: 0 }, ...}// Or receive one at a timeconstmsg=awaitrx.recv();// awaits next message

Mutex — Async-Safe Shared State (like Rust's Mutex<T>)

constcounter=Thread.mutex(0);// Mutex<number>// Spawn 10 threads, each incrementing the counterconsthandles=Array.from({length: 10},()=>Thread.spawn(async()=>{for(leti=0;i<1000;i++){awaitcounter.lock(value=>value+1);// async lock + transform}}));// Await all threadsawaitPromise.all(handles.map(h=>h.join()));console.log(awaitcounter.value());// 10000 — no data races

Async/Await Patterns — Full Integration

Threads are first-class async citizens. Every thread API returns a Promise, so they compose naturally with existing async patterns:

// Parallel async computationsasyncfunctionprocessAll(items){consthandles=items.map(item=>Thread.spawn(async()=>{constresult=awaitheavyCompute(item);returnresult;}));// Await all results — runs truly in parallel, not just concurrentreturnPromise.all(handles.map(h=>h.join()));}// Try/catch works across threadstry{consthandle=Thread.spawn(()=>{thrownewError("Thread error!");});awaithandle.join();}catch(e){console.error(e.message);// "Thread error!" — propagated}// Race between threadsconstfastest=awaitPromise.race([Thread.spawn(()=>computeRouteA(data)).join(),Thread.spawn(()=>computeRouteB(data)).join(),]);// AbortController integrationconstcontroller=newAbortController();consthandle=Thread.spawn(async(signal)=>{while(!signal.aborted){awaitdoWork();}},{signal: controller.signal});// Later: controller.abort();

Under the Hood: Truly Non-Blocking await

When you await a thread operation (like handle.join() or tx.send()), it does not block the underlying OS thread. Instead:

  1. It yields the current V8 Isolate execution back to the event loop.
  2. The OS thread is immediately freed and returned to the work-stealing pool to execute other pending tasks.
  3. When the awaited operation completes in the background, your JS task is re-queued and resumes execution.

This means you can spawn 100,000 threads with Thread.spawn and await them all, and it will only ever consume a small number of actual OS threads (equal to your pool size).

Automatic Parallelism

When the engine detects independent work, it automatically distributes across the thread pool — no API changes needed:

// Promise.all — independent promises run on separate pool threadsconst[users,orders,analytics]=awaitPromise.all([fetchUsers(),fetchOrders(),computeAnalytics(),]);// Array.parallelMap — data parallelism across threadsconstresults=await[1,2,3,4,5,6,7,8].parallelMap(async(n)=>{returnawaitheavyTransform(n);// each runs on a pool thread});// Array.parallelFilterconstvalid=awaitdata.parallelFilter(async(item)=>{returnawaitexpensiveValidation(item);});// Array.parallelReduce — tree-based parallel reductionconsttotal=awaitnumbers.parallelReduce(async(a,b)=>a+b,0);

Objects, Classes, and OOP

Because V8 isolates operate on a "shared-nothing" memory model, threads use Structured Cloning to pass data. This perfectly deep-copies plain objects (POJOs), Arrays, and Maps, but strips functions, methods, and prototypes.

If you want to use Object-Oriented Programming across threads, you have two options:

  1. Rehydration: Send plain object data across the thread boundary, and re-wrap it in a Class instance on the receiving end.
  2. Shared Memory: Back your Class state with a SharedArrayBuffer so multiple threads can safely mutate the exact same memory in parallel.
classPlayer{constructor(data){Object.assign(this,data);}attack(){console.log(this.name+" attacks!");}}constp=newPlayer({name: "Arthur"});Thread.spawn((rawPlayerData)=>{// Rehydrate the plain object back into a Class instanceconstworkerPlayer=newPlayer(rawPlayerData);workerPlayer.attack();// ✅ Works perfectly!},p);// `p` is sent as a plain object stripped of methods

Building with Multithreading

Multithreading is opt-in. Enable it with the v8_enable_multithreading GN flag:

# Generate build files with multithreading enabled
gn gen out/x64.release --args='v8_enable_multithreading=true'# Build
ninja -C out/x64.release d8
# Run a script using threads
out/x64.release/d8 --enable-multithreading my_script.js

Build Flags

FlagDefaultDescription
v8_enable_multithreadingfalseEnable the threading runtime and JS API
v8_thread_pool_size0 (auto)Number of pool threads. 0 = hardware_concurrency

Node.js Integration

Tip

Want to test it out? You can use the pre-configured custom Node.js repository ready for testing: shadowofleaf96/custom-node.

This experimental multithreading engine can be embedded directly into Node.js, allowing native multithreading in your Node.js applications.

To build Node.js with V8 multithreading support:

  1. Clone the Node.js repository (git clone https://github.com/nodejs/node.git).
  2. Replace the deps/v8 directory in the Node.js source tree with this customized V8 repository.
  3. Configure the Node.js build with the multithreading flag enabled:
    # On Windows (requires Visual Studio with C++ Clang Compiler and Rust)
    .\vcbuild.bat --enable-v8-multithreading
    # On POSIX (Linux/macOS)
    ./configure --enable-v8-multithreading
    make -j8
  4. This will automatically compile V8's multithreading components and link them into the Node.js binary. The threading APIs (Thread.spawn, Thread.channel, etc.) will be exposed natively within the Node.js environment.

Platform Support

PlatformArchitectureStatus
Linuxx64, arm64✅ Supported
macOSx64, arm64✅ Supported
Windowsx64✅ Supported

Design Principles

  1. Safety by default — No shared mutable state. Data races are impossible without explicit opt-in (SharedArrayBuffer).
  2. Zero-cost when unused — Multithreading is behind a build flag. No runtime overhead when disabled.
  3. Async-native — Every thread operation is a Promise. No callback hell, no blocking the event loop.
  4. Rust-inspired, JS-idiomatic — Familiar API patterns from Rust's std::thread, std::sync::mpsc, and std::sync::Mutex, but adapted for JavaScript's async/await ecosystem.

Changelog

Memory Optimization & Diagnostics Update (July 2026)

  • Zero-Copy SharedArrayBuffer: Added native SharedArrayBuffer support to Thread.spawn, allowing threads to instantly share and modify memory in parallel without transfer or copying overhead.
  • Unified Cross-Thread Stack Traces: Errors thrown inside worker threads automatically capture and append the main thread's caller stack frame (Thread.spawn call site) for seamless debugging across thread boundaries.
  • V8 Native Task Integration: Integrated ThreadPool task tracking into v8::Isolate::HasPendingBackgroundTasks(), seamlessly supporting top-level await in embedders and d8.
  • Zero-Copy ArrayBuffer Transfer: Added the ability to transfer ArrayBuffer objects between threads using the { transfer: [buffer] } option in Thread.spawn and tx.send, eliminating the overhead of copying large memory structures.
  • Dynamic Pool Sizing: Introduced Thread.getPoolSize() and Thread.setPoolSize(n) builtins to dynamically scale the thread pool up and down. The underlying deque arrays also automatically shrink to reclaim memory when idle.
  • Bounded Channels: Thread.channel(capacity) now accepts a capacity limit. Full channels exert back-pressure, pausing the Promise of the sender instead of infinitely queuing messages in memory.
  • Lazy Worker Initialization: Worker threads now lazily initialize their v8::Isolate environments and are constrained to strict heap size limits (2MB initial, 16MB maximum), dramatically reducing baseline RAM consumption.
  • Multithreading Engine Stability Fixes: Resolved isolate mismatch sandbox crashes in cross-thread Channel and Mutex promise resolution, corrected a parameter passing bug in parallel array iteration chunks, and fixed a microtask queue lifecycle task leak in the ThreadPool that caused d8 to hang on exit.

Initial Multithreading Release

  • Core Engine: Introduced the Chase-Lev work-stealing thread pool directly into the V8 runtime.
  • Thread Control: Added Thread.spawn, Thread.join, and non-blocking Thread.sleep.
  • Concurrency Primitives: Introduced safe message-passing Channels and shared-state Mutex constructs.
  • Automatic Parallelism: Added Array.prototype.parallelMap, Array.prototype.parallelFilter, Array.prototype.parallelReduce, and parallel task execution within Promise.all().

Contributing

Please follow the instructions mentioned at v8.dev/docs/contribute.

About

Multithreading Support in V8 Engine

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages