Skip to content

Commit c1708ea

Browse files
Jarred-Sumner190n
andauthored
Try bringing release/acquire heap access back (#16865)
Co-authored-by: Ben Grant <[email protected]>
1 parent 4471212 commit c1708ea

File tree

9 files changed

+68
-50
lines changed

9 files changed

+68
-50
lines changed

packages/bun-usockets/src/eventing/epoll_kqueue.c

+4-8
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ void us_loop_run(struct us_loop_t *loop) {
247247
}
248248
}
249249

250-
extern int Bun__JSC_onBeforeWait(void*);
250+
extern void Bun__JSC_onBeforeWait(void*);
251251
extern void Bun__JSC_onAfterWait(void*);
252252

253253
void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout) {
@@ -265,10 +265,8 @@ void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout
265265
/* Emit pre callback */
266266
us_internal_loop_pre(loop);
267267

268-
int needs_after_wait = 0;
269-
if (loop->data.jsc_vm) {
270-
needs_after_wait = Bun__JSC_onBeforeWait(loop->data.jsc_vm);
271-
}
268+
/* Safe if jsc_vm is NULL */
269+
Bun__JSC_onBeforeWait(loop->data.jsc_vm);
272270

273271
/* Fetch ready polls */
274272
#ifdef LIBUS_USE_EPOLL
@@ -280,9 +278,7 @@ void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout
280278
} while (IS_EINTR(loop->num_ready_polls));
281279
#endif
282280

283-
if (needs_after_wait) {
284-
Bun__JSC_onAfterWait(loop->data.jsc_vm);
285-
}
281+
Bun__JSC_onAfterWait(loop->data.jsc_vm);
286282

287283
/* Iterate ready polls, dispatching them by type */
288284
for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) {

src/bun.js/api/Timer.zig

+4
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,10 @@ pub const WTFTimer = struct {
958958
return this;
959959
}
960960

961+
pub export fn WTFTimer__runIfImminent(vm: *VirtualMachine) void {
962+
vm.eventLoop().runImminentGCTimer();
963+
}
964+
961965
pub fn run(this: *WTFTimer, vm: *VirtualMachine) void {
962966
if (this.event_loop_timer.state == .ACTIVE) {
963967
vm.timer.remove(&this.event_loop_timer);
+22-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
11
#include "root.h"
2+
#include "BunClientData.h"
23

34
#include <JavaScriptCore/VM.h>
45
#include <JavaScriptCore/Heap.h>
56

6-
extern "C" int Bun__JSC_onBeforeWait(JSC::VM* vm)
7+
// It would be nicer to construct a DropAllLocks in us_loop_run_bun_tick (the only function that
8+
// uses onBeforeWait and onAfterWait), but that code is in C. We use an optional as that lets us
9+
// check whether it's initialized.
10+
static thread_local std::optional<JSC::JSLock::DropAllLocks> drop_all_locks { std::nullopt };
11+
12+
extern "C" void WTFTimer__runIfImminent(void* bun_vm);
13+
14+
// Safe if VM is nullptr
15+
extern "C" void Bun__JSC_onBeforeWait(JSC::VM* vm)
716
{
8-
(void)vm;
9-
// if (vm->heap.hasAccess()) {
10-
// vm->heap.releaseAccess();
11-
// return 1;
12-
// }
13-
return 0;
17+
ASSERT(!drop_all_locks.has_value());
18+
if (vm) {
19+
bool previouslyHadAccess = vm->heap.hasHeapAccess();
20+
drop_all_locks.emplace(*vm);
21+
if (previouslyHadAccess) {
22+
vm->heap.releaseAccess();
23+
}
24+
}
1425
}
1526

1627
extern "C" void Bun__JSC_onAfterWait(JSC::VM* vm)
1728
{
18-
(void)vm;
19-
// vm->heap.acquireAccess();
29+
if (vm) {
30+
vm->heap.acquireAccess();
31+
drop_all_locks.reset();
32+
}
2033
}

src/bun.js/bindings/BunProcess.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -1068,10 +1068,18 @@ static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& e
10681068
}
10691069

10701070
if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) {
1071-
#if !OS(WINDOWS)
1071+
#if OS(LINUX)
1072+
// SIGKILL and SIGSTOP cannot be handled, and JSC needs its own signal handler to
1073+
// suspend and resume the JS thread which we must not override.
1074+
if (signalNumber != SIGKILL && signalNumber != SIGSTOP && signalNumber != g_wtfConfig.sigThreadSuspendResume) {
1075+
#elif OS(DARWIN)
1076+
// these signals cannot be handled
10721077
if (signalNumber != SIGKILL && signalNumber != SIGSTOP) {
1078+
#elif OS(WINDOWS)
1079+
// windows has no SIGSTOP
1080+
if (signalNumber != SIGKILL) {
10731081
#else
1074-
if (signalNumber != SIGKILL) { // windows has no SIGSTOP
1082+
#error unknown OS
10751083
#endif
10761084

10771085
if (isAdded) {

src/bun.js/bindings/ZigGlobalObject.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
#include "JSX509Certificate.h"
162162
#include "JSS3File.h"
163163
#include "S3Error.h"
164+
#include <JavaScriptCore/JSBasePrivate.h>
164165
#if ENABLE(REMOTE_INSPECTOR)
165166
#include "JavaScriptCore/RemoteInspectorServer.h"
166167
#endif
@@ -236,6 +237,22 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c
236237
return;
237238
has_loaded_jsc = true;
238239
JSC::Config::enableRestrictedOptions();
240+
#if OS(LINUX)
241+
{
242+
// By default, JavaScriptCore's garbage collector sends SIGUSR1 to the JS thread to suspend
243+
// and resume it in order to scan its stack memory. Whatever signal it uses can't be
244+
// reliably intercepted by JS code, and several npm packages use SIGUSR1 for various
245+
// features. We tell it to use SIGPWR instead, which we assume is unlikely to be reliable
246+
// for its stated purpose. Mono's garbage collector also uses SIGPWR:
247+
// https://www.mono-project.com/docs/advanced/embedding/#signal-handling
248+
//
249+
// This call needs to be before most of the other JSC initialization, as we can't
250+
// reconfigure which signal is used once the signal handler has already been registered.
251+
bool configure_signal_success = JSConfigureSignalForGC(SIGPWR);
252+
ASSERT(configure_signal_success);
253+
ASSERT(g_wtfConfig.sigThreadSuspendResume == SIGPWR);
254+
}
255+
#endif
239256

240257
std::set_terminate([]() { Zig__GlobalObject__onCrash(); });
241258
WTF::initializeMainThread();

src/bun.js/bindings/bindings.zig

-19
Original file line numberDiff line numberDiff line change
@@ -6209,25 +6209,6 @@ pub const VM = extern struct {
62096209
LargeHeap = 1,
62106210
};
62116211

6212-
extern fn Bun__JSC_onBeforeWait(vm: *VM) i32;
6213-
extern fn Bun__JSC_onAfterWait(vm: *VM) void;
6214-
pub const ReleaseHeapAccess = struct {
6215-
vm: *VM,
6216-
needs_to_acquire: bool,
6217-
pub fn acquire(this: *const ReleaseHeapAccess) void {
6218-
if (this.needs_to_acquire) {
6219-
Bun__JSC_onAfterWait(this.vm);
6220-
}
6221-
}
6222-
};
6223-
6224-
/// Temporarily give up access to the heap, allowing other work to proceed. Call acquire() on
6225-
/// the return value at scope exit. If you did not already have heap access, release and acquire
6226-
/// are both safe no-ops.
6227-
pub fn releaseHeapAccess(vm: *VM) ReleaseHeapAccess {
6228-
return .{ .vm = vm, .needs_to_acquire = Bun__JSC_onBeforeWait(vm) != 0 };
6229-
}
6230-
62316212
pub fn create(heap_type: HeapType) *VM {
62326213
return cppFn("create", .{@intFromEnum(heap_type)});
62336214
}

src/bun.js/event_loop.zig

+8-6
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,12 @@ pub const EventLoop = struct {
14101410
}
14111411
}
14121412

1413+
pub fn runImminentGCTimer(this: *EventLoop) void {
1414+
if (this.imminent_gc_timer.swap(null, .seq_cst)) |timer| {
1415+
timer.run(this.virtual_machine);
1416+
}
1417+
}
1418+
14131419
pub fn tickConcurrentWithCount(this: *EventLoop) usize {
14141420
this.updateCounts();
14151421

@@ -1419,9 +1425,7 @@ pub const EventLoop = struct {
14191425
}
14201426
}
14211427

1422-
if (this.imminent_gc_timer.swap(null, .seq_cst)) |timer| {
1423-
timer.run(this.virtual_machine);
1424-
}
1428+
this.runImminentGCTimer();
14251429

14261430
var concurrent = this.concurrent_tasks.popBatch();
14271431
const count = concurrent.count;
@@ -1492,9 +1496,7 @@ pub const EventLoop = struct {
14921496
}
14931497
}
14941498

1495-
if (this.imminent_gc_timer.swap(null, .seq_cst)) |timer| {
1496-
timer.run(ctx);
1497-
}
1499+
this.runImminentGCTimer();
14981500

14991501
if (loop.isActive()) {
15001502
this.processGCTimer();

src/bun.js/module_loader.zig

-6
Original file line numberDiff line numberDiff line change
@@ -1660,12 +1660,6 @@ pub const ModuleLoader = struct {
16601660
var parse_result: ParseResult = switch (disable_transpilying or
16611661
(loader == .json and !path.isJSONCFile())) {
16621662
inline else => |return_file_only| brk: {
1663-
const heap_access = if (!disable_transpilying)
1664-
jsc_vm.jsc.releaseHeapAccess()
1665-
else
1666-
JSC.VM.ReleaseHeapAccess{ .vm = jsc_vm.jsc, .needs_to_acquire = false };
1667-
defer heap_access.acquire();
1668-
16691663
break :brk jsc_vm.transpiler.parseMaybeReturnFileOnly(
16701664
parse_options,
16711665
null,

src/linux_c.zig

+3
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,9 @@ export fn sys_epoll_pwait2(epfd: i32, events: ?[*]std.os.linux.epoll_event, maxe
642642
@bitCast(@as(isize, @intCast(maxevents))),
643643
@intFromPtr(timeout),
644644
@intFromPtr(sigmask),
645+
// This is the correct value. glibc claims to pass `sizeof sigset_t` for this argument,
646+
// which would be 128, but they actually pass 8 which is what the kernel expects.
647+
// https://github.com/ziglang/zig/issues/12715
645648
8,
646649
),
647650
);

0 commit comments

Comments
 (0)