1 /***********************************************************
2 Copyright (c) 2000, BeOpen.com.
3 Copyright (c) 1995-2000, Corporation for National Research Initiatives.
4 Copyright (c) 1990-1995, Stichting Mathematisch Centrum.
7 See the file "Misc/COPYRIGHT" for information on usage and
8 redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
9 ******************************************************************/
11 /* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */
12 /* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */
18 typedef struct NRMUTEX
{
22 } NRMUTEX
, *PNRMUTEX
;
25 typedef PVOID WINAPI
interlocked_cmp_xchg_t(PVOID
*dest
, PVOID exc
, PVOID comperand
) ;
27 /* Sorry mate, but we haven't got InterlockedCompareExchange in Win95! */
28 static PVOID WINAPI
interlocked_cmp_xchg(PVOID
*dest
, PVOID exc
, PVOID comperand
)
30 static LONG spinlock
= 0 ;
34 /* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */
35 while(InterlockedExchange(&spinlock
, 1))
37 // Using Sleep(0) can cause a priority inversion.
38 // Sleep(0) only yields the processor if there's
39 // another thread of the same priority that's
40 // ready to run. If a high-priority thread is
41 // trying to acquire the lock, which is held by
42 // a low-priority thread, then the low-priority
43 // thread may never get scheduled and hence never
44 // free the lock. NT attempts to avoid priority
45 // inversions by temporarily boosting the priority
46 // of low-priority runnable threads, but the problem
47 // can still occur if there's a medium-priority
48 // thread that's always runnable. If Sleep(1) is used,
49 // then the thread unconditionally yields the CPU. We
50 // only do this for the second and subsequent even
51 // iterations, since a millisecond is a long time to wait
52 // if the thread can be scheduled in again sooner
53 // (~100,000 instructions).
54 // Avoid priority inversion: 0, 1, 0, 1,...
59 if (result
== comperand
)
61 /* Release spinlock */
66 static interlocked_cmp_xchg_t
*ixchg
;
67 BOOL
InitializeNonRecursiveMutex(PNRMUTEX mutex
)
71 /* Sorely, Win95 has no InterlockedCompareExchange API (Win98 has), so we have to use emulation */
72 HANDLE kernel
= GetModuleHandle("kernel32.dll") ;
73 if (!kernel
|| (ixchg
= (interlocked_cmp_xchg_t
*)GetProcAddress(kernel
, "InterlockedCompareExchange")) == NULL
)
74 ixchg
= interlocked_cmp_xchg
;
77 mutex
->owned
= -1 ; /* No threads have entered NonRecursiveMutex */
78 mutex
->thread_id
= 0 ;
79 mutex
->hevent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
) ;
80 return mutex
->hevent
!= NULL
; /* TRUE if the mutex is created */
83 #ifdef InterlockedCompareExchange
84 #undef InterlockedCompareExchange
86 #define InterlockedCompareExchange(dest,exchange,comperand) (ixchg((dest), (exchange), (comperand)))
88 VOID
DeleteNonRecursiveMutex(PNRMUTEX mutex
)
91 CloseHandle(mutex
->hevent
) ;
92 mutex
->hevent
= NULL
; /* Just in case */
95 DWORD
EnterNonRecursiveMutex(PNRMUTEX mutex
, BOOL wait
)
97 /* Assume that the thread waits successfully */
100 /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
103 if (InterlockedCompareExchange((PVOID
*)&mutex
->owned
, (PVOID
)0, (PVOID
)-1) != (PVOID
)-1)
104 return WAIT_TIMEOUT
;
105 ret
= WAIT_OBJECT_0
;
108 ret
= InterlockedIncrement(&mutex
->owned
) ?
109 /* Some thread owns the mutex, let's wait... */
110 WaitForSingleObject(mutex
->hevent
, INFINITE
) : WAIT_OBJECT_0
;
112 mutex
->thread_id
= GetCurrentThreadId() ; /* We own it */
116 BOOL
LeaveNonRecursiveMutex(PNRMUTEX mutex
)
118 /* We don't own the mutex */
119 mutex
->thread_id
= 0 ;
121 InterlockedDecrement(&mutex
->owned
) < 0 ||
122 SetEvent(mutex
->hevent
) ; /* Other threads are waiting, wake one on them up */
125 PNRMUTEX
AllocNonRecursiveMutex(void)
127 PNRMUTEX mutex
= (PNRMUTEX
)malloc(sizeof(NRMUTEX
)) ;
128 if (mutex
&& !InitializeNonRecursiveMutex(mutex
))
136 void FreeNonRecursiveMutex(PNRMUTEX mutex
)
140 DeleteNonRecursiveMutex(mutex
) ;
145 long PyThread_get_thread_ident(void);
148 * Change all headers to pure ANSI as no one will use K&R style on an
153 * Initialization of the C package, should not be needed.
155 static void PyThread__init_thread(void)
162 int PyThread_start_new_thread(void (*func
)(void *), void *arg
)
167 dprintf(("%ld: PyThread_start_new_thread called\n", PyThread_get_thread_ident()));
169 PyThread_init_thread();
171 rv
= _beginthread(func
, 0, arg
); /* use default stack size */
175 dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", PyThread_get_thread_ident(), rv
));
182 * Return the thread Id instead of an handle. The Id is said to uniquely identify the
183 * thread in the system
185 long PyThread_get_thread_ident(void)
188 PyThread_init_thread();
190 return GetCurrentThreadId();
193 static void do_PyThread_exit_thread(int no_cleanup
)
195 dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident()));
204 void PyThread_exit_thread(void)
206 do_PyThread_exit_thread(0);
209 void PyThread__exit_thread(void)
211 do_PyThread_exit_thread(1);
215 static void do_PyThread_exit_prog(int status
, int no_cleanup
)
217 dprintf(("PyThread_exit_prog(%d) called\n", status
));
225 void PyThread_exit_prog(int status
)
227 do_PyThread_exit_prog(status
, 0);
230 void PyThread__exit_prog(int status
)
232 do_PyThread_exit_prog(status
, 1);
234 #endif /* NO_EXIT_PROG */
237 * Lock support. It has too be implemented as semaphores.
238 * I [Dag] tried to implement it with mutex but I could find a way to
239 * tell whether a thread already own the lock or not.
241 PyThread_type_lock
PyThread_allocate_lock(void)
245 dprintf(("PyThread_allocate_lock called\n"));
247 PyThread_init_thread();
249 aLock
= AllocNonRecursiveMutex() ;
251 dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock
));
253 return (PyThread_type_lock
) aLock
;
256 void PyThread_free_lock(PyThread_type_lock aLock
)
258 dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock
));
260 FreeNonRecursiveMutex(aLock
) ;
264 * Return 1 on success if the lock was acquired
266 * and 0 if the lock was not acquired. This means a 0 is returned
267 * if the lock has already been acquired by this thread!
269 int PyThread_acquire_lock(PyThread_type_lock aLock
, int waitflag
)
273 dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock
, waitflag
));
275 success
= aLock
&& EnterNonRecursiveMutex((PNRMUTEX
) aLock
, (waitflag
== 1 ? INFINITE
: 0)) == WAIT_OBJECT_0
;
277 dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock
, waitflag
, success
));
282 void PyThread_release_lock(PyThread_type_lock aLock
)
284 dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock
));
286 if (!(aLock
&& LeaveNonRecursiveMutex((PNRMUTEX
) aLock
)))
287 dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", PyThread_get_thread_ident(), aLock
, GetLastError()));
293 PyThread_type_sema
PyThread_allocate_sema(int value
)
297 dprintf(("%ld: PyThread_allocate_sema called\n", PyThread_get_thread_ident()));
299 PyThread_init_thread();
301 aSemaphore
= CreateSemaphore( NULL
, /* Security attributes */
302 value
, /* Initial value */
303 INT_MAX
, /* Maximum value */
304 NULL
); /* Name of semaphore */
306 dprintf(("%ld: PyThread_allocate_sema() -> %p\n", PyThread_get_thread_ident(), aSemaphore
));
308 return (PyThread_type_sema
) aSemaphore
;
311 void PyThread_free_sema(PyThread_type_sema aSemaphore
)
313 dprintf(("%ld: PyThread_free_sema(%p) called\n", PyThread_get_thread_ident(), aSemaphore
));
315 CloseHandle((HANDLE
) aSemaphore
);
319 XXX must do something about waitflag
321 int PyThread_down_sema(PyThread_type_sema aSemaphore
, int waitflag
)
325 dprintf(("%ld: PyThread_down_sema(%p) called\n", PyThread_get_thread_ident(), aSemaphore
));
327 waitResult
= WaitForSingleObject( (HANDLE
) aSemaphore
, INFINITE
);
329 dprintf(("%ld: PyThread_down_sema(%p) return: %l\n", PyThread_get_thread_ident(), aSemaphore
, waitResult
));
333 void PyThread_up_sema(PyThread_type_sema aSemaphore
)
336 (HANDLE
) aSemaphore
, /* Handle of semaphore */
337 1, /* increment count by one */
338 NULL
); /* not interested in previous count */
340 dprintf(("%ld: PyThread_up_sema(%p)\n", PyThread_get_thread_ident(), aSemaphore
));