Skip to content

Commit fff08ec

Browse files
author
guido
committed
Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru,
who wrote: Here's the new version of thread_nt.h. More particular, there is a new version of thread lock that uses kernel object (e.g. semaphore) only in case of contention; in other case it simply uses interlocked functions, which are faster by the order of magnitude. It doesn't make much difference without threads present, but as soon as thread machinery initialised and (mostly) the interpreter global lock is on, difference becomes tremendous. I've included a small script, which initialises threads and launches pystone. With original thread_nt.h, Pystone results with initialised threads are twofold worse then w/o threads. With the new version, only 10% worse. I have used this patch for about 6 months (with threaded and non-threaded applications). It works remarkably well (though I'd desperately prefer Python was free-threaded; I hope, it will soon). git-svn-id: http://svn.python.org/projects/python/trunk@15345 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent ddf7524 commit fff08ec

1 file changed

Lines changed: 113 additions & 25 deletions

File tree

Python/thread_nt.h

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,113 @@ PERFORMANCE OF THIS SOFTWARE.
3030
******************************************************************/
3131

3232
/* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */
33+
/* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */
3334

3435
#include <windows.h>
3536
#include <limits.h>
3637
#include <process.h>
3738

39+
typedef struct NRMUTEX {
40+
LONG owned ;
41+
DWORD thread_id ;
42+
HANDLE hevent ;
43+
} NRMUTEX, *PNRMUTEX ;
44+
45+
46+
typedef PVOID WINAPI interlocked_cmp_xchg_t(PVOID *dest, PVOID exc, PVOID comperand) ;
47+
48+
/* Sorry mate, but we haven't got InterlockedCompareExchange in Win95! */
49+
static PVOID WINAPI interlocked_cmp_xchg(PVOID *dest, PVOID exc, PVOID comperand)
50+
{
51+
static LONG spinlock = 0 ;
52+
PVOID result ;
53+
54+
/* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */
55+
while(InterlockedExchange(&spinlock, 1)) Sleep(0) ;
56+
result = *dest ;
57+
if (result == comperand)
58+
*dest = exc ;
59+
/* Release spinlock */
60+
spinlock = 0 ;
61+
return result ;
62+
} ;
63+
64+
static interlocked_cmp_xchg_t *ixchg ;
65+
BOOL InitializeNonRecursiveMutex(PNRMUTEX mutex)
66+
{
67+
if (!ixchg)
68+
{
69+
/* Sorely, Win95 has no InterlockedCompareExchange API (Win98 has), so we have to use emulation */
70+
HANDLE kernel = GetModuleHandle("kernel32.dll") ;
71+
if (!kernel || (ixchg = (interlocked_cmp_xchg_t *)GetProcAddress(kernel, "InterlockedCompareExchange")) == NULL)
72+
ixchg = interlocked_cmp_xchg ;
73+
}
74+
75+
mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */
76+
mutex->thread_id = 0 ;
77+
mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
78+
return mutex->hevent != NULL ; /* TRUE if the mutex is created */
79+
}
80+
81+
#define InterlockedCompareExchange(dest,exchange,comperand) (ixchg((dest), (exchange), (comperand)))
82+
83+
VOID DeleteNonRecursiveMutex(PNRMUTEX mutex)
84+
{
85+
/* No in-use check */
86+
CloseHandle(mutex->hevent) ;
87+
mutex->hevent = NULL ; /* Just in case */
88+
}
89+
90+
DWORD EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
91+
{
92+
/* Assume that the thread waits successfully */
93+
DWORD ret ;
94+
95+
/* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
96+
if (!wait)
97+
{
98+
if (InterlockedCompareExchange((PVOID *)&mutex->owned, (PVOID)0, (PVOID)-1) != (PVOID)-1)
99+
return WAIT_TIMEOUT ;
100+
ret = WAIT_OBJECT_0 ;
101+
}
102+
else
103+
ret = InterlockedIncrement(&mutex->owned) ?
104+
/* Some thread owns the mutex, let's wait... */
105+
WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
106+
107+
mutex->thread_id = GetCurrentThreadId() ; /* We own it */
108+
return ret ;
109+
}
110+
111+
BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex)
112+
{
113+
/* We don't own the mutex */
114+
mutex->thread_id = 0 ;
115+
return
116+
InterlockedDecrement(&mutex->owned) < 0 ||
117+
SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */
118+
}
119+
120+
PNRMUTEX AllocNonRecursiveMutex()
121+
{
122+
PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ;
123+
if (mutex && !InitializeNonRecursiveMutex(mutex))
124+
{
125+
free(mutex) ;
126+
mutex = NULL ;
127+
}
128+
return mutex ;
129+
}
130+
131+
void FreeNonRecursiveMutex(PNRMUTEX mutex)
132+
{
133+
if (mutex)
134+
{
135+
DeleteNonRecursiveMutex(mutex) ;
136+
free(mutex) ;
137+
}
138+
}
139+
38140
long PyThread_get_thread_ident(void);
39141

40142
/*
@@ -65,7 +167,7 @@ int PyThread_start_new_thread(void (*func)(void *), void *arg)
65167

66168
if (rv != -1) {
67169
success = 1;
68-
dprintf(("%ld: PyThread_start_new_thread succeeded: %ld\n", PyThread_get_thread_ident(), rv));
170+
dprintf(("%ld: PyThread_start_new_thread succeeded: %ld\n", PyThread_get_thread_ident(), aThreadId));
69171
}
70172

71173
return success;
@@ -79,7 +181,7 @@ long PyThread_get_thread_ident(void)
79181
{
80182
if (!initialized)
81183
PyThread_init_thread();
82-
184+
83185
return GetCurrentThreadId();
84186
}
85187

@@ -133,17 +235,13 @@ void PyThread__exit_prog _P1(int status)
133235
*/
134236
PyThread_type_lock PyThread_allocate_lock(void)
135237
{
136-
HANDLE aLock;
238+
PNRMUTEX aLock;
137239

138240
dprintf(("PyThread_allocate_lock called\n"));
139241
if (!initialized)
140242
PyThread_init_thread();
141243

142-
aLock = CreateSemaphore(NULL, /* Security attributes */
143-
1, /* Initial value */
144-
1, /* Maximum value */
145-
NULL);
146-
/* Name of semaphore */
244+
aLock = AllocNonRecursiveMutex() ;
147245

148246
dprintf(("%ld: PyThread_allocate_lock() -> %lx\n", PyThread_get_thread_ident(), (long)aLock));
149247

@@ -154,7 +252,7 @@ void PyThread_free_lock(PyThread_type_lock aLock)
154252
{
155253
dprintf(("%ld: PyThread_free_lock(%lx) called\n", PyThread_get_thread_ident(),(long)aLock));
156254

157-
CloseHandle((HANDLE) aLock);
255+
FreeNonRecursiveMutex(aLock) ;
158256
}
159257

160258
/*
@@ -165,16 +263,11 @@ void PyThread_free_lock(PyThread_type_lock aLock)
165263
*/
166264
int PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
167265
{
168-
int success = 1;
169-
DWORD waitResult;
266+
int success ;
170267

171268
dprintf(("%ld: PyThread_acquire_lock(%lx, %d) called\n", PyThread_get_thread_ident(),(long)aLock, waitflag));
172269

173-
waitResult = WaitForSingleObject((HANDLE) aLock, (waitflag == 1 ? INFINITE : 0));
174-
175-
if (waitResult != WAIT_OBJECT_0) {
176-
success = 0; /* We failed */
177-
}
270+
success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag == 1 ? INFINITE : 0)) == WAIT_OBJECT_0 ;
178271

179272
dprintf(("%ld: PyThread_acquire_lock(%lx, %d) -> %d\n", PyThread_get_thread_ident(),(long)aLock, waitflag, success));
180273

@@ -185,13 +278,8 @@ void PyThread_release_lock(PyThread_type_lock aLock)
185278
{
186279
dprintf(("%ld: PyThread_release_lock(%lx) called\n", PyThread_get_thread_ident(),(long)aLock));
187280

188-
if (!ReleaseSemaphore(
189-
(HANDLE) aLock, /* Handle of semaphore */
190-
1, /* increment count by one */
191-
NULL)) /* not interested in previous count */
192-
{
281+
if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
193282
dprintf(("%ld: Could not PyThread_release_lock(%lx) error: %l\n", PyThread_get_thread_ident(), (long)aLock, GetLastError()));
194-
}
195283
}
196284

197285
/*
@@ -206,9 +294,9 @@ PyThread_type_sema PyThread_allocate_sema(int value)
206294
PyThread_init_thread();
207295

208296
aSemaphore = CreateSemaphore( NULL, /* Security attributes */
209-
value, /* Initial value */
210-
INT_MAX, /* Maximum value */
211-
NULL); /* Name of semaphore */
297+
value, /* Initial value */
298+
INT_MAX, /* Maximum value */
299+
NULL); /* Name of semaphore */
212300

213301
dprintf(("%ld: PyThread_allocate_sema() -> %lx\n", PyThread_get_thread_ident(), (long)aSemaphore));
214302

0 commit comments

Comments
 (0)