Skip to content

Uncontended semaphore acquire loops can stall the garbage collector #182

Closed

Description

@dimensionscape

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions