Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
06b6446
Bump WebKit: DeferTermination outside a VMEntryScope; wake Atomics.wa…
dylan-conway Aug 14, 2026
de02641
tests: reword the Atomics.wait test comment (not a released regression)
dylan-conway Aug 14, 2026
1f5ccf2
worker: act on terminate() that lands while draining work scheduled b…
dylan-conway Aug 14, 2026
ee97acc
node:path binding: check for an exception before storing each createP…
dylan-conway Aug 14, 2026
26b46fc
Keep JSC's termination-request flag set for as long as a stopped work…
dylan-conway Aug 14, 2026
35fe928
Merge remote-tracking branch 'origin/main' into claude/webkit-termina…
dylan-conway Aug 14, 2026
d3fc0de
test: clarify why the Atomics.wait test pauses before terminate()
dylan-conway Aug 14, 2026
70d049e
Merge remote-tracking branch 'origin/main' into claude/worker-thread-…
dylan-conway Aug 14, 2026
06fd21a
Merge remote-tracking branch 'origin/main' into claude/worker-thread-…
dylan-conway Aug 14, 2026
2affab3
Merge remote-tracking branch 'origin/main' into claude/webkit-termina…
dylan-conway Aug 14, 2026
89a8dad
An empty rejection value yields an inert promise; re-check the stop a…
dylan-conway Aug 14, 2026
08d3a8e
Merge remote-tracking branch 'origin/claude/webkit-termination-fixes'…
dylan-conway Aug 14, 2026
0f0d1b3
Bun::createError always yields an object, even with a worker's termin…
dylan-conway Aug 14, 2026
78e82b9
crypto: prime generation/checking gives up once its worker has been a…
dylan-conway Aug 14, 2026
06a36ca
Keep the termination request at every Rust exception-check boundary, …
dylan-conway Aug 14, 2026
dda2c50
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 14, 2026
0743d0e
on_before_exit: a stop requested during the drain also suppresses the…
dylan-conway Aug 14, 2026
6de3d1e
Merge remote-tracking branch 'origin/claude/worker-thread-fixes-2' in…
dylan-conway Aug 14, 2026
d1ad280
Subprocess: no pending-activity bookkeeping once the wrapper is final…
dylan-conway Aug 14, 2026
3f9e935
test: give the Atomics.wait terminate test the file's timeout
dylan-conway Aug 14, 2026
d3479d9
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 14, 2026
9fcb814
TopExceptionScope: the pure exception() query keeps the termination r…
dylan-conway Aug 14, 2026
8baa168
Merge remote-tracking branch 'origin/claude/worker-thread-fixes-2' in…
dylan-conway Aug 14, 2026
b876956
fetch: hold a counted ref on the response ByteStream source while pro…
dylan-conway Aug 14, 2026
7e57f3e
valkey: close() returns what a half-open socket's onclose left pendin…
dylan-conway Aug 14, 2026
023e042
valkey: close unconditionally before combining results (clippy or_fun…
dylan-conway Aug 14, 2026
e7ea225
test: the streaming-fetch worker-exit test proves each worker reached…
dylan-conway Aug 14, 2026
bd0bffc
fetch: drop ignore_remaining_response_body's now-meaningless from_fin…
dylan-conway Aug 14, 2026
af391d8
crypto: generatePrime's completion treats a pending exception as auth…
dylan-conway Aug 14, 2026
34dc046
Merge remote-tracking branch 'origin/main' into claude/worker-thread-…
dylan-conway Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/build/deps/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* for local mode. Override via `--webkit-version=<hash>` to test a branch.
* From https://github.com/oven-sh/WebKit releases.
*/
export const WEBKIT_VERSION = "e2f13c6aa1cdaa885722c0cb55e609334a717d13";
export const WEBKIT_VERSION = "f0f60fd2324817dae9656d8bf2fcae25ceaccc37";

/**
* WebKit (JavaScriptCore) — the JS engine.
Expand Down
28 changes: 23 additions & 5 deletions src/jsc/TopExceptionScope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,22 @@ impl ExceptionValidationScope {
// so the validation scope's diagnostics point at the user's call site, and the
// scope is RAII (dropped on every return path including `?`).

unsafe extern "C" {
// safe fn: `&JSGlobalObject` is ABI-identical to a non-null `JSGlobalObject*`; C++ only reads
// and writes its VM's termination flag.
safe fn Bun__VM__keepTerminationRequestWithPendingException(global: &JSGlobalObject);
}

/// An FFI call into JSC came back with an exception pending. If it is a worker's
/// TerminationException that we are about to leave pending past the entry it unwound (see the C++
/// side), keep JSC's termination-request flag in step with it. Cold path only.
#[cold]
#[inline(never)]
fn thrown(global: &JSGlobalObject) -> JsError {
Bun__VM__keepTerminationRequestWithPendingException(global);
JsError::Thrown
}
Comment thread
dylan-conway marked this conversation as resolved.
Comment thread
dylan-conway marked this conversation as resolved.

/// `[[ZIG_EXPORT(zero_is_throw)]]`: callee returns `JSValue::ZERO` ⟺ it threw.
///
/// `src` is the diagnostic location for `BUN_JSC_dumpSimulatedThrows`; pass [`src!`](crate::src)
Expand All @@ -600,7 +616,7 @@ pub fn call_zero_is_throw_at(
let v = f();
scope.assert_exception_presence_matches(v == JSValue::ZERO);
if v == JSValue::ZERO {
Err(JsError::Thrown)
Err(thrown(global))
} else {
Ok(v)
}
Expand Down Expand Up @@ -629,7 +645,7 @@ pub fn call_false_is_throw_at(
let mut scope = ExceptionValidationScope::init_guard_at(&mut storage, global, src);
let v = f();
scope.assert_exception_presence_matches(!v);
if v { Ok(()) } else { Err(JsError::Thrown) }
if v { Ok(()) } else { Err(thrown(global)) }
}

/// `[[ZIG_EXPORT(false_is_throw)]]` — `#[track_caller]` convenience wrapper.
Expand All @@ -650,7 +666,7 @@ pub fn call_null_is_throw_at<T>(
let mut scope = ExceptionValidationScope::init_guard_at(&mut storage, global, src);
let v = f();
scope.assert_exception_presence_matches(v.is_null());
NonNull::new(v).ok_or(JsError::Thrown)
NonNull::new(v).ok_or_else(|| thrown(global))
}

/// `[[ZIG_EXPORT(null_is_throw)]]` — `#[track_caller]` convenience wrapper.
Expand Down Expand Up @@ -681,7 +697,9 @@ pub fn call_check_slow_at<R>(
let mut storage = core::mem::MaybeUninit::uninit();
let mut scope = TopExceptionScope::init_guard_at(&mut storage, global, src);
let r = f();
scope.return_if_exception()?;
if scope.return_if_exception().is_err() {
return Err(thrown(global));
}
Ok(r)
}
#[cfg(not(any(debug_assertions, bun_asan)))]
Expand All @@ -692,7 +710,7 @@ pub fn call_check_slow_at<R>(
// wrapper (reads `vm.m_exception` with trap check; same body as
// `RETURN_IF_EXCEPTION` in C++).
if crate::cpp::Bun__RETURN_IF_EXCEPTION(global) {
Err(JsError::Thrown)
Err(thrown(global))
} else {
Ok(r)
}
Expand Down
10 changes: 10 additions & 0 deletions src/jsc/VirtualMachine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,17 @@ impl VirtualMachine {
let mut dispatch = false;
loop {
while self.is_event_loop_alive() {
// A stop requested meanwhile (worker.terminate(), or process.exit()
// from a listener) ends the drain, as it ends the worker's main
// loop: what is still in flight is cancelled by teardown, and its
// completions would no longer be delivered to release the loop.
if !self.script_allowed() {
return;
}
self.tick();
if !self.script_allowed() {
return;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
self.auto_tick_active();
dispatch = true;
}
Expand Down
33 changes: 23 additions & 10 deletions src/jsc/bindings/ErrorCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,26 @@ JSObject* ErrorCodeCache::createError(VM& vm, Zig::GlobalObject* globalObject, E
}

auto* structure = uncheckedDowncast<Structure>(cache->internalField(static_cast<unsigned>(code)).get());
auto* created_error = JSC::ErrorInstance::create(globalObject, structure, message, options, nullptr, JSC::RuntimeType::TypeNothing, data.type, true);

// Convert the message and `cause` here rather than in ErrorInstance::create(JSGlobalObject*, ...),
// which hands back nullptr when that conversion is interrupted: every caller throws or rejects with
// what we return, so an object is always made. Whatever interrupted the conversion is dealt with
// below.
String messageString = message.isUndefined() ? String() : message.toWTFString(globalObject);
JSValue cause;
if (options.isObject() && !scope.exception())
cause = asObject(options)->getIfPropertyExists(globalObject, vm.propertyNames->cause);
if (auto* thrown_exception = scope.exception()) [[unlikely]] {
(void)scope.tryClearException();
if (vm.hasPendingTerminationException()) [[unlikely]]
return created_error;
// TODO investigate what can throw here and whether it will throw non-objects
// (this is better than before where we would have returned nullptr from createError if any
// exception were thrown by ErrorInstance::create)
return uncheckedDowncast<JSObject>(thrown_exception->value());
// A stopped worker's TerminationException stays pending for the caller's frame to report; the
// (message-less) error is still made. Anything else thrown while building the message (an
// OOM resolving a rope, a throwing `cause` getter) becomes the error, as before.
if (!vm.isTerminationException(thrown_exception)) {
(void)scope.tryClearException();
if (auto* object = thrown_exception->value().getObject())
return object;
}
}
return created_error;
return JSC::ErrorInstance::create(vm, structure, messageString, cause, nullptr, JSC::RuntimeType::TypeNothing, data.type, true);
}

JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, const String& message)
Expand All @@ -240,7 +249,11 @@ JSObject* createError(VM& vm, JSC::JSGlobalObject* globalObject, ErrorCode code,
return createError(vm, zigGlobalObject, code, message, jsUndefined());

auto* structure = createErrorStructure(vm, globalObject, errors[static_cast<size_t>(code)].type, errors[static_cast<size_t>(code)].name, errors[static_cast<size_t>(code)].code);
return JSC::ErrorInstance::create(globalObject, structure, message, jsUndefined(), nullptr, JSC::RuntimeType::TypeNothing, errors[static_cast<size_t>(code)].type, true);
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
String messageString = message.isUndefined() ? String() : message.toWTFString(globalObject);
if (scope.exception() && !vm.hasPendingTerminationException())
(void)scope.tryClearException();
return JSC::ErrorInstance::create(vm, structure, messageString, JSValue(), nullptr, JSC::RuntimeType::TypeNothing, errors[static_cast<size_t>(code)].type, true);
}

JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options)
Expand Down
4 changes: 4 additions & 0 deletions src/jsc/bindings/NodeTimerObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#include <JavaScriptCore/ObjectConstructor.h>
#include "JavaScriptCore/JSCJSValue.h"
#include "AsyncContextFrame.h"

extern "C" void Bun__VM__keepTerminationRequestWithPendingException(JSC::JSGlobalObject*);

namespace Bun {
using namespace JSC;

Expand Down Expand Up @@ -63,6 +66,7 @@ static bool call(JSGlobalObject* globalObject, JSValue timerObject, JSValue call
auto* exception = scope.exception();
(void)scope.tryClearException();
Bun__reportUnhandledError(globalObject, JSValue::encode(exception));
Bun__VM__keepTerminationRequestWithPendingException(globalObject);
hadException = true;
}

Expand Down
14 changes: 6 additions & 8 deletions src/jsc/bindings/Path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,13 @@ JSC::JSValue createNodePathBinding(Zig::GlobalObject* globalObject)
auto scope = DECLARE_THROW_SCOPE(vm);
auto binding = constructEmptyArray(globalObject, nullptr, 2);
RETURN_IF_EXCEPTION(scope, {});
binding->putDirectIndex(
globalObject,
(unsigned)0,
Zig::createPath(globalObject, false));
auto* posix = Zig::createPath(globalObject, false);
RETURN_IF_EXCEPTION(scope, {});
binding->putDirectIndex(
globalObject,
(unsigned)1,
Zig::createPath(globalObject, true));
binding->putDirectIndex(globalObject, (unsigned)0, posix);
RETURN_IF_EXCEPTION(scope, {});
auto* win32 = Zig::createPath(globalObject, true);
RETURN_IF_EXCEPTION(scope, {});
binding->putDirectIndex(globalObject, (unsigned)1, win32);
RETURN_IF_EXCEPTION(scope, {});
return binding;
}
Expand Down
5 changes: 5 additions & 0 deletions src/jsc/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3272,13 +3272,16 @@ extern "C" [[ZIG_EXPORT(nothrow)]] double JSC__JSGlobalObject__jsDateNow(JSC::JS

// ====================== end conditional builtin globals ======================

extern "C" void Bun__VM__keepTerminationRequestWithPendingException(JSC::JSGlobalObject*);

uint8_t GlobalObject::drainMicrotasks()
{
auto& vm = this->vm();
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);

if (auto* exception = scope.exception()) [[unlikely]] {
if (vm.isTerminationException(exception)) [[unlikely]] {
Bun__VM__keepTerminationRequestWithPendingException(this);
return 1;
}

Expand All @@ -3301,6 +3304,7 @@ uint8_t GlobalObject::drainMicrotasks()
nextTickQueue->drain(vm, this);
if (auto* exception = scope.exception()) {
if (vm.isTerminationException(exception)) {
Bun__VM__keepTerminationRequestWithPendingException(this);
return 1;
}
(void)scope.tryClearException();
Expand All @@ -3311,6 +3315,7 @@ uint8_t GlobalObject::drainMicrotasks()
vm.drainMicrotasks();
if (auto* exception = scope.exception()) {
if (vm.isTerminationException(exception)) {
Bun__VM__keepTerminationRequestWithPendingException(this);
return 1;
}
(void)scope.tryClearException();
Expand Down
27 changes: 25 additions & 2 deletions src/jsc/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3892,9 +3892,18 @@ JSC::EncodedJSValue JSC__JSPromise__wrap(JSC::JSGlobalObject* globalObject, void
arg0->rejectAsHandled(vm, JSC::JSValue::decode(JSValue2));
}

JSC::JSPromise* JSC__JSPromise__rejectedPromise(JSC::JSGlobalObject* arg0, JSC::EncodedJSValue JSValue1)
JSC::JSPromise* JSC__JSPromise__rejectedPromise(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue JSValue1)
{
return JSC::JSPromise::rejectedPromise(arg0, JSC::JSValue::decode(JSValue1));
auto value = JSC::JSValue::decode(JSValue1);
if (!value) [[unlikely]] {
// Building the rejection value threw — a stopped worker's pending TerminationException cuts
// error creation short. That exception is what the caller's frame reports; hand back an
// inert promise rather than reject with nothing.
auto& vm = JSC::getVM(globalObject);
ASSERT(vm.exceptionForInspection());
return JSC::JSPromise::create(vm, globalObject->promiseStructure());
}
return JSC::JSPromise::rejectedPromise(globalObject, value);
}

[[ZIG_EXPORT(check_slow)]] void JSC__JSPromise__resolve(JSC::JSPromise* arg0, JSC::JSGlobalObject* arg1, JSC::EncodedJSValue JSValue2)
Expand Down Expand Up @@ -6632,6 +6641,20 @@ extern "C" double Bun__JSC__operationMathPow(double x, double y)
return operationMathPow(x, y);
}

// A stopped worker's TerminationException is kept pending after the JS entry it unwound has
// returned, until teardown clears or re-arms it (Bun__GlobalObject__clearExceptionsForExit /
// Zig__GlobalObject__forbidExecution). JSC resets its "termination in progress" flag when the
// outermost VMEntryScope exits and expects the two to agree while the exception is pending
// (VMTraps::deferTerminationSlow, VM::setException); its own clients never keep the exception past
// that point without also ceasing to touch the VM. Called where an entry has just come back with an
// exception: keep the flag for as long as we keep the exception.
extern "C" void Bun__VM__keepTerminationRequestWithPendingException(JSC::JSGlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);
if (vm.hasPendingTerminationException() && !vm.hasTerminationRequest()) [[unlikely]]
vm.setHasTerminationRequest();
}

#if !ENABLE(EXCEPTION_SCOPE_VERIFICATION)
extern "C" [[ZIG_EXPORT(nothrow)]] __attribute__((__always_inline__)) bool Bun__RETURN_IF_EXCEPTION(JSC::JSGlobalObject* globalObject)
{
Expand Down
47 changes: 27 additions & 20 deletions src/jsc/bindings/node/crypto/CryptoPrimes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@
#include "helpers.h"
#include "CryptoUtil.h"
#include "NodeValidator.h"
#include "BunClientData.h"

namespace Bun {

using namespace ncrypto;
using namespace JSC;

// BN_generate_prime_ex / BN_is_prime_ex progress callback: returning false aborts. `safe` primes and
// awkward `add`/`rem` constraints can take unboundedly long, so stop as soon as the VM this is for has
// been asked to stop (worker terminate() / process.exit()) rather than hold its teardown — or, on the
// sync paths, the termination itself — until OpenSSL happens to finish. Readable from any thread.
static BignumPointer::PrimeCheckCallback whileScriptAllowed(JSGlobalObject* globalObject)
{
return [clientData = WebCore::clientData(globalObject->vm())](int, int) -> bool {
return clientData->scriptAllowed();
};
}

CheckPrimeJobCtx::CheckPrimeJobCtx(ncrypto::BignumPointer candidate, int32_t checks)
: m_candidate(WTF::move(candidate))
, m_checks(checks)
Expand All @@ -25,20 +37,19 @@ extern "C" void Bun__CheckPrimeJobCtx__runTask(CheckPrimeJobCtx* ctx, JSGlobalOb
}
void CheckPrimeJobCtx::runTask(JSGlobalObject* lexicalGlobalObject)
{
auto res = m_candidate.isPrime(m_checks, [](int32_t a, int32_t b) -> bool {
// TODO(dylan-conway): ideally we check for !vm->isShuttingDown() here
return true;
});

m_result = res != 0;
auto res = m_candidate.isPrime(m_checks, whileScriptAllowed(lexicalGlobalObject));
m_failed = res < 0;
m_result = res > 0;
}

extern "C" void Bun__CheckPrimeJobCtx__runFromJS(CheckPrimeJobCtx* ctx, JSGlobalObject* lexicalGlobalObject, JSCallbackArgs* out)
{
*out = ctx->runFromJS(lexicalGlobalObject);
}
JSCallbackArgs CheckPrimeJobCtx::runFromJS(JSGlobalObject*)
JSCallbackArgs CheckPrimeJobCtx::runFromJS(JSGlobalObject* globalObject)
{
if (m_failed) [[unlikely]]
return { createError(globalObject, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "could not check prime"_s) };
return { jsUndefined(), jsBoolean(m_result) };
}

Expand Down Expand Up @@ -97,12 +108,11 @@ JSC_DEFINE_HOST_FUNCTION(jsCheckPrimeSync, (JSC::JSGlobalObject * lexicalGlobalO
}
}

auto res = candidate.isPrime(checks, [](int32_t a, int32_t b) -> bool {
// TODO(dylan-conway): ideally we check for !vm->isShuttingDown() here
return true;
});
auto res = candidate.isPrime(checks, whileScriptAllowed(lexicalGlobalObject));
if (res < 0) [[unlikely]]
return ERR::CRYPTO_OPERATION_FAILED(scope, lexicalGlobalObject, "could not check prime"_s);

return JSValue::encode(jsBoolean(res != 0));
return JSValue::encode(jsBoolean(res > 0));
}

JSC_DEFINE_HOST_FUNCTION(jsCheckPrime, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
Expand Down Expand Up @@ -177,10 +187,7 @@ extern "C" void Bun__GeneratePrimeJobCtx__runTask(GeneratePrimeJobCtx* ctx, JSGl
}
void GeneratePrimeJobCtx::runTask(JSGlobalObject* lexicalGlobalObject)
{
m_prime.generate({ .bits = m_size, .safe = m_safe, .add = m_add, .rem = m_rem }, [](int32_t a, int32_t b) -> bool {
// TODO(dylan-conway): ideally we check for !vm->isShuttingDown() here
return true;
});
m_failed = !m_prime.generate({ .bits = m_size, .safe = m_safe, .add = m_add, .rem = m_rem }, whileScriptAllowed(lexicalGlobalObject));
}

extern "C" void Bun__GeneratePrimeJobCtx__runFromJS(GeneratePrimeJobCtx* ctx, JSGlobalObject* lexicalGlobalObject, JSCallbackArgs* out)
Expand All @@ -192,6 +199,8 @@ JSCallbackArgs GeneratePrimeJobCtx::runFromJS(JSGlobalObject* globalObject)
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);

if (m_failed) [[unlikely]]
return { createError(globalObject, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "could not generate prime"_s) };
JSValue result = GeneratePrimeJob::result(globalObject, scope, m_prime, m_bigint);
EXCEPTION_ASSERT(result.isEmpty() == !!scope.exception());
if (scope.exception()) [[unlikely]] {
Expand Down Expand Up @@ -458,10 +467,8 @@ JSC_DEFINE_HOST_FUNCTION(jsGeneratePrimeSync, (JSC::JSGlobalObject * lexicalGlob
return ERR::CRYPTO_OPERATION_FAILED(scope, lexicalGlobalObject, "could not generate prime"_s);
}

prime.generate({ .bits = size, .safe = safe, .add = add, .rem = rem }, [](int32_t a, int32_t b) -> bool {
// TODO(dylan-conway): ideally we check for !vm->isShuttingDown() here
return true;
});
if (!prime.generate({ .bits = size, .safe = safe, .add = add, .rem = rem }, whileScriptAllowed(lexicalGlobalObject))) [[unlikely]]
return ERR::CRYPTO_OPERATION_FAILED(scope, lexicalGlobalObject, "could not generate prime"_s);

return JSValue::encode(GeneratePrimeJob::result(lexicalGlobalObject, scope, prime, bigint));
}
Expand Down
2 changes: 2 additions & 0 deletions src/jsc/bindings/node/crypto/CryptoPrimes.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct CheckPrimeJobCtx {
ncrypto::BignumPointer m_candidate;

bool m_result { false };
bool m_failed { false };

WTF_MAKE_TZONE_ALLOCATED(CheckPrimeJobCtx);
};
Expand All @@ -42,6 +43,7 @@ struct GeneratePrimeJobCtx {
ncrypto::BignumPointer m_add;
ncrypto::BignumPointer m_rem;
ncrypto::BignumPointer m_prime;
bool m_failed { false };

WTF_MAKE_TZONE_ALLOCATED(GeneratePrimeJobCtx);
};
Expand Down
Loading