Sync Primitives Research: SRW Spin, Adaptive CS, Condvar PI

Date: 2026-04-15 Status: B (SRW spin) and C1 (pi_cond requeue-PI) IMPLEMENTED. A (CS DYNAMIC_SPIN) planned. C2 (SRW PI) deferred.


1. Windows Behavior (what apps expect)

SRW Locks

Critical Sections

WaitOnAddress

Condition Variables


2. Linux/glibc Behavior (what our platform provides)

glibc Adaptive Mutex (PTHREAD_MUTEX_ADAPTIVE_NP)

glibc pthread_rwlock

Linux Kernel rwsem (CONFIG_RWSEM_SPIN_ON_OWNER)

Linux Kernel Mutex Adaptive Spin (CONFIG_MUTEX_SPIN_ON_OWNER)

PI for Read-Write Locks — State of the Art


3. Upstream Wine vs Wine-NSPA Implementation

SRW Locks (dlls/ntdll/sync.c)

Aspect Upstream Wine Wine-NSPA (v5)
Spin count 0 — straight to RtlWaitOnAddress 256 iterations (commit 005b55b4d8d)
RT behavior Same as normal threads RT threads skip spin entirely (SCHED_FIFO starvation prevention)
Wait path RtlWaitOnAddress → FIFO queue → futex_wait Same (spin is before this path)
PI support None None (SRW PI deferred — unsolved problem)

Bit layout (both): struct srw_lock { short exclusive_waiters; unsigned short owners; } = 4 bytes. Wait is via RtlWaitOnAddress → user-space FIFO queue → NtWaitForAlertByThreadId → futex_wait. No spin in RtlWaitOnAddress itself — the spin phase is in the acquire functions.

Critical Sections (dlls/ntdll/sync.c)

Aspect Upstream Wine Wine-NSPA
Contended path Keyed event wait (NtWaitForKeyedEvent) FUTEX_LOCK_PI (kernel rt_mutex PI chain)
Spin loop Fixed YieldProcessor, bailout on LockCount > 0 Same spin loop, PI path handles contention
DYNAMIC_SPIN flag FIXME stub FIXME stub (planned)
PI support None Full PI via nspa_cs_enter_pi()

Condition Variables (dlls/ntdll/sync.c + libs/librtpi/rtpi.h)

Aspect Upstream Wine Wine-NSPA (v5)
Win32 condvar (RtlSleepConditionVariableCS) RtlWaitOnAddress, no PI Same (no PI — uses RtlWaitOnAddress, not pi_cond)
Unix-side condvar (pi_cond_t) Not present FUTEX_WAIT_REQUEUE_PI (commit 43862d8b591)
pi_cond consumers N/A ntdll file I/O, virtual memory, audio drivers, gstreamer
PI gap on wake N/A Closed — kernel atomically requeues waiter onto PI mutex

RtlWaitOnAddress (dlls/ntdll/sync.c)


4. librtpi Integration

What librtpi provides

Wine-NSPA integration


5. Comparison Table

Implementation Spin Count Algorithm Owner-Aware PI Support
Windows SRW ~1024 (fixed) test + pause loop No No
Windows CS 0 or caller-set (DYNAMIC_SPIN: ~2000-4000) test + YieldProcessor No No (kernel Mutant only)
glibc adaptive mutex 100 (fixed) atomic_load + pause No Separate (PTHREAD_PRIO_INHERIT)
glibc rwlock 0 (no spinning) Direct futex wait No No
Kernel mutex Unbounded (owner-tracking) OSQ + spin while owner on CPU Yes Via rt_mutex on PREEMPT_RT
Kernel rwsem (writer) Unbounded (owner) + 10-25us (reader-owned) OSQ + owner tracking + time cap Yes Partial (writer only) on PREEMPT_RT
Upstream Wine SRW 0 (no spinning) Direct RtlWaitOnAddress No No
Wine-NSPA SRW 256 (fixed, RT skips) test + YieldProcessor No No
Upstream Wine CS Static (caller-set) YieldProcessor + early bailout No No
Wine-NSPA CS Static (caller-set) YieldProcessor + early bailout No FUTEX_LOCK_PI
Wine-NSPA pi_cond N/A FUTEX_WAIT_REQUEUE_PI N/A Requeue-PI (atomic)

6. Remaining Design Work

A — CS DYNAMIC_SPIN (Planned)

Implement RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN properly: - Substitute system default spincount (~4000, matching Windows) - Gate behind !nspa_cs_pi_active() — when PI is active, the kernel handles contention better than userspace spinning - Low priority since the PI path is the primary contention mechanism

C2 — SRW PI (Deferred Indefinitely)

SRW locks have no owner by design. PI requires an owner. This is unsolved even in the Linux kernel: - No FUTEX_RDLOCK_PI or equivalent exists - PREEMPT_RT’s rwsem approach (serialize writers through rt_mutex) has known reader-starvation issues - Windows itself has no SRW PI — apps don’t expect it

Decision: Don’t pursue. Focus PI effort on CS (done) and condvar (done). For RT paths that need PI, apps should use CriticalSection not SRW.


7. Implementation Status

  1. B — SRW spin phaseIMPLEMENTED (commit 005b55b4d8d)

  2. C1 — pi_cond requeue-PIIMPLEMENTED (commit 43862d8b591)

  3. A — CS DYNAMIC_SPINPlanned

  4. C2 — SRW PIDeferred indefinitely


8. Open Questions