fix: remove mutex/io dependency from the dsl#20
Conversation
| var locked: std.atomic.Value(bool) = std.atomic.Value(bool).init(false); | ||
|
|
||
| fn lock() void { | ||
| while (locked.cmpxchgWeak(false, true, .acquire, .monotonic) != null) { |
There was a problem hiding this comment.
For someone curiosity how this code works, here is some explanation.
CPUs provide a single hardware instruction that does two things atomically (cannot be interrupted):
▎ "Read the value. If it equals X, replace it with Y. Tell me if it worked."
This is cmpxchg — it's a single CPU instruction, not two separate operations. The CPU's cache coherency protocol guarantees that only one core can win the race.
The signature is:
locked.cmpxchgWeak(expected_value, new_value, success_order, fail_order)
So as per call we instruct
- success_case: If
lockedvalue isfalse, set it totrue- means mark the resource locked. - fail_case: If
lockedvalue is notfalse, keep trying in the loop. - In
success_caseinstruct atomic state to beacquire, so no CPU instruction is allowed to re-ordered in memory. - In
fail_caseinstruct atomic state to bemonotonic, that suggest to perform any atomic operation afterwards.
|
|
||
| fn lock() void { | ||
| while (locked.cmpxchgWeak(false, true, .acquire, .monotonic) != null) { | ||
| std.atomic.spinLoopHint(); |
There was a problem hiding this comment.
We can add noop here inside loop or add sleep. In both cases CPU resources are exhausted.
spinLoopHint() — Emits a PAUSE instruction (x86) or YIELD (ARM). This tells the CPU don't waste power or starve the sibling hyperthread. Without this, the tight loop burns CPU unnecessarily.
|
If it doesn't provide Io, the user function also can't use any Io related functions(e.g, Timer, sleep, deadline, std.Io.File) such as https://github.com/ChainSafe/zapi/pull/9/changes#diff-73bd664bb3f77929a0c3aa8d7af66c6ed2008813ab8eef8e43030b33f7d8510eR212, is it a problem? |
Replace
std.Thread.Mutexwith an atomic spinlock in the DSL class registry, eliminating the need to threadstd.Iothrough the public API when migrating to Zig 0.16.Motivation
Zig 0.16 replaces
std.Thread.Mutexwithstd.Io.Mutex, which requires anIoinstance forlock(io)/unlock(io). PR #9 addresses this by adding a mandatory.iooptionto
exportModuleand threadingIothroughregisterDecls→registerClass→ the internal mutex.This is problematic because:
Iois an unrelated runtime concernexportModuleAPI for every DSL consumerApproach
The class registry mutex protects a short linked list with very low contention:
materializeClassInstance(when a DSL function returns a class)A spinlock using
std.atomic.Value(bool)+cmpxchgWeakis ideal here — nanosecond critical sections, near-zero contention, and zero dependency onstd.Threadorstd.Io.The public API remains unchanged.
Changes
src/js/class_runtime.zig: Replacestd.Thread.Mutexwith atomic spinlock (lock/unlockusingcmpxchgWeak+spinLoopHint)export_module.zig, examples, or any user-facing API