[Patches] re: Yakov's patch to thread_nt.h

Bill Tutt billtut@microsoft.com
Thu, 11 May 2000 00:36:13 -0700


Calling Sleep(0) for a spinlock can cause a priority inversion.

Here's a patch to fix that, with comments explaining whats going on.

The only other possible tweak that came to mind was adding a spinlock with a
max count of 500 or so before actually hitting the WaitForSingleObject()
call in EnterNonRecursiveMutex(). 
Ring transitions into kernel mode are exspensive, so try and avoid them when
possible. :)

Index: thread_nt.h
===================================================================
RCS file: /projects/cvsroot/python/dist/src/Python/thread_nt.h,v
retrieving revision 2.8
diff -u -r2.8 thread_nt.h
--- thread_nt.h	2000/05/05 14:29:59	2.8
+++ thread_nt.h	2000/05/11 07:24:28
@@ -50,9 +50,32 @@
 {
 	static LONG spinlock = 0 ;
 	PVOID result ;
+	DWORD dwSleep = 0;
 
 	/* Acqire spinlock (yielding control to other threads if cant aquire
for the moment) */
-	while(InterlockedExchange(&spinlock, 1)) Sleep(0) ;
+	while(InterlockedExchange(&spinlock, 1))
+	{
+		// Using Sleep(0) can cause a priority inversion.
+		// Sleep(0) only yields the processor if there's
+		// another thread of the same priority that's
+		// ready to run.  If a high-priority thread is
+		// trying to acquire the lock, which is held by
+		// a low-priority thread, then the low-priority
+		// thread may never get scheduled and hence never
+		// free the lock.  NT attempts to avoid priority
+		// inversions by temporarily boosting the priority
+		// of low-priority runnable threads, but the problem
+		// can still occur if there's a medium-priority
+		// thread that's always runnable.  If Sleep(1) is used,
+		// then the thread unconditionally yields the CPU.  We
+		// only do this for the second and subsequent even
+		// iterations, since a millisecond is a long time to wait
+		// if the thread can be scheduled in again sooner
+		// (~100,000 instructions).
+		// Avoid priority inversion: 0, 1, 0, 1,...
+		Sleep(dwSleep);
+		dwSleep = !dwSleep;
+	}
 	result = *dest ;
 	if (result == comperand)
 		*dest = exc ;



Release info:
I confirm that, to the best of my knowledge and belief, this contribution is
free of any claims of third parties under copyright, patent or other rights
or interests ("claims"). To the extent that I have any such claims, I hereby
grant to CNRI a nonexclusive, irrevocable, royalty-free, worldwide license
to reproduce, distribute, perform and/or display publicly, prepare
derivative versions, and otherwise use this contribution as part of the
Python software and its related documentation, or any derivative versions
thereof, at no cost to CNRI or its licensed users, and to authorize others
to do so. I acknowledge that CNRI may, at its sole discretion, decide
whether or not to incorporate this contribution in the Python software and
its related documentation. I further grant CNRI permission to use my name
and other identifying information provided to CNRI by me for use in
connection with the Python software and its related documentation. 

Bill