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
V fork: sync.Mutex.lock() silently ignores pthread_mutex_lock failure — uninitialized mutexes provide no exclusion (Darwin)
Problem
vlib/sync/sync_default.c.v:
@[inline]
pub fn (mut m Mutex) lock() {
C.pthread_mutex_lock(&m.mutex)
}
The return value is discarded. On Darwin, pthread_mutex_lock on a
zero-initialized pthread_mutex_t returns EINVAL (Darwin's PTHREAD_MUTEX_INITIALIZER carries _PTHREAD_MUTEX_SIG_init, not all-zeros),
so a sync.Mutex that was never init()'d — e.g. any VALUE-typed __global,
which _vinit zeroes — becomes a silent no-op lock: no error, no panic, no
mutual exclusion. On Linux/glibc zeroed storage happens to equal PTHREAD_MUTEX_INITIALIZER, so the same code locks correctly there — the bug
ships only on macOS and presents as an "intermittent flake".
Two SSE subscriber-registry locks were value-typed __global sync.Mutex.
Under parallel-suite CPU load, two executor threads interleaved inside the
"critical section" and a concurrent array append lost an element (server
trace showed both subscribes printing the same corrupted subs=[10, 0] at the
same millisecond). Root-caused and fixed on the cx side by converting to &sync.Mutex + sync.new_mutex() (cx-private PR for vlang#303); this issue is the
V-layer footgun that let it fail silently.
Suggested hardening (pick one or more)
Cheapest: assert/panic on nonzero return in lock()/unlock() (debug
builds at minimum): an EINVAL here is always a programming error and
currently costs days of intermittent-flake debugging.
-d sync_checked (or default in debug): use PTHREAD_MUTEX_ERRORCHECK
attrs + checked returns.
Checker warning for VALUE-typed sync.Mutex/sync.RwMutex in __global
blocks (zeroed in _vinit, unusable on Darwin) — suggest &sync.Mutex + sync.new_mutex() in a module init().
Upstream
This is not CX-specific — the discard is in upstream vlib/sync/sync_default.c.v
and bites any V program on macOS. Candidate for the next upstream bundle
(precedent: vlang#27458, vlang#27658): options 1 and 2 are V-only and
CX-agnostic; option 3 (checker warning) likewise. Fix here first, then include
in the bundle rather than filing upstream ad hoc.
Repro sketch
__global ( m sync.Mutex counter int )
// spawn N threads: m.lock(); counter++; m.unlock()// macOS: lost updates (lock is a no-op). Linux: correct.
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.
V fork: sync.Mutex.lock() silently ignores pthread_mutex_lock failure — uninitialized mutexes provide no exclusion (Darwin)
Problem
vlib/sync/sync_default.c.v:The return value is discarded. On Darwin,
pthread_mutex_lockon azero-initialized
pthread_mutex_treturnsEINVAL(Darwin'sPTHREAD_MUTEX_INITIALIZERcarries_PTHREAD_MUTEX_SIG_init, not all-zeros),so a
sync.Mutexthat was neverinit()'d — e.g. any VALUE-typed__global,which
_vinitzeroes — becomes a silent no-op lock: no error, no panic, nomutual exclusion. On Linux/glibc zeroed storage happens to equal
PTHREAD_MUTEX_INITIALIZER, so the same code locks correctly there — the bugships only on macOS and presents as an "intermittent flake".
Field impact (cx-private vlang#303)
Two SSE subscriber-registry locks were value-typed
__global sync.Mutex.Under parallel-suite CPU load, two executor threads interleaved inside the
"critical section" and a concurrent array append lost an element (server
trace showed both subscribes printing the same corrupted
subs=[10, 0]at thesame millisecond). Root-caused and fixed on the cx side by converting to
&sync.Mutex+sync.new_mutex()(cx-private PR for vlang#303); this issue is theV-layer footgun that let it fail silently.
Suggested hardening (pick one or more)
lock()/unlock()(debugbuilds at minimum): an
EINVALhere is always a programming error andcurrently costs days of intermittent-flake debugging.
-d sync_checked(or default in debug): usePTHREAD_MUTEX_ERRORCHECKattrs + checked returns.
sync.Mutex/sync.RwMutexin__globalblocks (zeroed in
_vinit, unusable on Darwin) — suggest&sync.Mutex+sync.new_mutex()in a moduleinit().Upstream
This is not CX-specific — the discard is in upstream
vlib/sync/sync_default.c.vand bites any V program on macOS. Candidate for the next upstream bundle
(precedent: vlang#27458, vlang#27658): options 1 and 2 are V-only and
CX-agnostic; option 3 (checker warning) likewise. Fix here first, then include
in the bundle rather than filing upstream ad hoc.
Repro sketch
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.