@@ -50,9 +50,32 @@ static PVOID WINAPI interlocked_cmp_xchg(PVOID *dest, PVOID exc, PVOID comperand
5050{
5151 static LONG spinlock = 0 ;
5252 PVOID result ;
53+ DWORD dwSleep = 0 ;
5354
5455 /* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */
55- while (InterlockedExchange (& spinlock , 1 )) Sleep (0 ) ;
56+ while (InterlockedExchange (& spinlock , 1 ))
57+ {
58+ // Using Sleep(0) can cause a priority inversion.
59+ // Sleep(0) only yields the processor if there's
60+ // another thread of the same priority that's
61+ // ready to run. If a high-priority thread is
62+ // trying to acquire the lock, which is held by
63+ // a low-priority thread, then the low-priority
64+ // thread may never get scheduled and hence never
65+ // free the lock. NT attempts to avoid priority
66+ // inversions by temporarily boosting the priority
67+ // of low-priority runnable threads, but the problem
68+ // can still occur if there's a medium-priority
69+ // thread that's always runnable. If Sleep(1) is used,
70+ // then the thread unconditionally yields the CPU. We
71+ // only do this for the second and subsequent even
72+ // iterations, since a millisecond is a long time to wait
73+ // if the thread can be scheduled in again sooner
74+ // (~100,000 instructions).
75+ // Avoid priority inversion: 0, 1, 0, 1,...
76+ Sleep (dwSleep );
77+ dwSleep = !dwSleep ;
78+ }
5679 result = * dest ;
5780 if (result == comperand )
5881 * dest = exc ;
0 commit comments