You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Uncontended semaphore acquire loops can stall the garbage collector #182
CountingSemaphore_obj::acquire() in CountingSemaphore.posix.cpp and CountingSemaphore.win32.cpp returns immediately when the semaphore is available, without checking gPauseForCollect. A thread repeatedly acquiring and releasing its semaphore can therefore keep running while the collector waits for it to become safe.
tryAcquire() already checks gPauseForCollect before its fast path. The Apple acquire implementation enters a GC-free zone, so it does not have this particular gap.
I reproduced this against checkout 793e054 on Linux x86_64 through WSL using Haxe 4.3.3 and HXCPP_M64. A native worker attaches with SetTopOfStack, constructs CountingSemaphore_obj(1), publishes readiness through a std::atomic, and runs:
while (!stop.load()) {
sem->acquire();
sem->release();
}
hx::UnregisterCurrentThread();
The main thread waits for readiness inside an AutoGCFreeZone and then times cpp.vm.Gc.run(true). An unattached watchdog thread sets stop after two seconds so the probe can finish instead of hanging.
Results:
acquire/release loop: collection took 2.00073 seconds, until the watchdog stopped the worker.
Control replacing acquire() with tryAcquire(null()): collection took 0.0006249 seconds while the loop continued.
Both probes joined the worker inside a GC-free zone afterward. The worker did no Haxe allocations in its loop, and there was no semaphore contention.
This leaves a collector starvation path after #119 fixed the polling variants. Add the same pending-collection check before the acquire fast path on POSIX and Windows, and a bounded regression that exercises an always-available semaphore. Test both backends and confirm the uncontended path remains fast.
Activity