|
1 |
|
2 /* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */ |
|
3 /* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */ |
|
4 /* Eliminated some memory leaks, gsw@agere.com */ |
|
5 |
|
6 #include <windows.h> |
|
7 #include <limits.h> |
|
8 #ifdef HAVE_PROCESS_H |
|
9 #include <process.h> |
|
10 #endif |
|
11 |
|
12 typedef struct NRMUTEX { |
|
13 LONG owned ; |
|
14 DWORD thread_id ; |
|
15 HANDLE hevent ; |
|
16 } NRMUTEX, *PNRMUTEX ; |
|
17 |
|
18 |
|
19 BOOL |
|
20 InitializeNonRecursiveMutex(PNRMUTEX mutex) |
|
21 { |
|
22 mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */ |
|
23 mutex->thread_id = 0 ; |
|
24 mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ; |
|
25 return mutex->hevent != NULL ; /* TRUE if the mutex is created */ |
|
26 } |
|
27 |
|
28 VOID |
|
29 DeleteNonRecursiveMutex(PNRMUTEX mutex) |
|
30 { |
|
31 /* No in-use check */ |
|
32 CloseHandle(mutex->hevent) ; |
|
33 mutex->hevent = NULL ; /* Just in case */ |
|
34 } |
|
35 |
|
36 DWORD |
|
37 EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait) |
|
38 { |
|
39 /* Assume that the thread waits successfully */ |
|
40 DWORD ret ; |
|
41 |
|
42 /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */ |
|
43 if (!wait) |
|
44 { |
|
45 if (InterlockedCompareExchange(&mutex->owned, 0, -1) != -1) |
|
46 return WAIT_TIMEOUT ; |
|
47 ret = WAIT_OBJECT_0 ; |
|
48 } |
|
49 else |
|
50 ret = InterlockedIncrement(&mutex->owned) ? |
|
51 /* Some thread owns the mutex, let's wait... */ |
|
52 WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ; |
|
53 |
|
54 mutex->thread_id = GetCurrentThreadId() ; /* We own it */ |
|
55 return ret ; |
|
56 } |
|
57 |
|
58 BOOL |
|
59 LeaveNonRecursiveMutex(PNRMUTEX mutex) |
|
60 { |
|
61 /* We don't own the mutex */ |
|
62 mutex->thread_id = 0 ; |
|
63 return |
|
64 InterlockedDecrement(&mutex->owned) < 0 || |
|
65 SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */ |
|
66 } |
|
67 |
|
68 PNRMUTEX |
|
69 AllocNonRecursiveMutex(void) |
|
70 { |
|
71 PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ; |
|
72 if (mutex && !InitializeNonRecursiveMutex(mutex)) |
|
73 { |
|
74 free(mutex) ; |
|
75 mutex = NULL ; |
|
76 } |
|
77 return mutex ; |
|
78 } |
|
79 |
|
80 void |
|
81 FreeNonRecursiveMutex(PNRMUTEX mutex) |
|
82 { |
|
83 if (mutex) |
|
84 { |
|
85 DeleteNonRecursiveMutex(mutex) ; |
|
86 free(mutex) ; |
|
87 } |
|
88 } |
|
89 |
|
90 long PyThread_get_thread_ident(void); |
|
91 |
|
92 /* |
|
93 * Initialization of the C package, should not be needed. |
|
94 */ |
|
95 static void |
|
96 PyThread__init_thread(void) |
|
97 { |
|
98 } |
|
99 |
|
100 /* |
|
101 * Thread support. |
|
102 */ |
|
103 |
|
104 typedef struct { |
|
105 void (*func)(void*); |
|
106 void *arg; |
|
107 long id; |
|
108 HANDLE done; |
|
109 } callobj; |
|
110 |
|
111 static int |
|
112 bootstrap(void *call) |
|
113 { |
|
114 callobj *obj = (callobj*)call; |
|
115 /* copy callobj since other thread might free it before we're done */ |
|
116 void (*func)(void*) = obj->func; |
|
117 void *arg = obj->arg; |
|
118 |
|
119 obj->id = PyThread_get_thread_ident(); |
|
120 ReleaseSemaphore(obj->done, 1, NULL); |
|
121 func(arg); |
|
122 return 0; |
|
123 } |
|
124 |
|
125 long |
|
126 PyThread_start_new_thread(void (*func)(void *), void *arg) |
|
127 { |
|
128 Py_uintptr_t rv; |
|
129 callobj obj; |
|
130 |
|
131 dprintf(("%ld: PyThread_start_new_thread called\n", |
|
132 PyThread_get_thread_ident())); |
|
133 if (!initialized) |
|
134 PyThread_init_thread(); |
|
135 |
|
136 obj.id = -1; /* guilty until proved innocent */ |
|
137 obj.func = func; |
|
138 obj.arg = arg; |
|
139 obj.done = CreateSemaphore(NULL, 0, 1, NULL); |
|
140 if (obj.done == NULL) |
|
141 return -1; |
|
142 |
|
143 rv = _beginthread(bootstrap, |
|
144 Py_SAFE_DOWNCAST(_pythread_stacksize, |
|
145 Py_ssize_t, int), |
|
146 &obj); |
|
147 if (rv == (Py_uintptr_t)-1) { |
|
148 /* I've seen errno == EAGAIN here, which means "there are |
|
149 * too many threads". |
|
150 */ |
|
151 dprintf(("%ld: PyThread_start_new_thread failed: %p errno %d\n", |
|
152 PyThread_get_thread_ident(), (void*)rv, errno)); |
|
153 obj.id = -1; |
|
154 } |
|
155 else { |
|
156 dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", |
|
157 PyThread_get_thread_ident(), (void*)rv)); |
|
158 /* wait for thread to initialize, so we can get its id */ |
|
159 WaitForSingleObject(obj.done, INFINITE); |
|
160 assert(obj.id != -1); |
|
161 } |
|
162 CloseHandle((HANDLE)obj.done); |
|
163 return obj.id; |
|
164 } |
|
165 |
|
166 /* |
|
167 * Return the thread Id instead of an handle. The Id is said to uniquely identify the |
|
168 * thread in the system |
|
169 */ |
|
170 long |
|
171 PyThread_get_thread_ident(void) |
|
172 { |
|
173 if (!initialized) |
|
174 PyThread_init_thread(); |
|
175 |
|
176 return GetCurrentThreadId(); |
|
177 } |
|
178 |
|
179 static void |
|
180 do_PyThread_exit_thread(int no_cleanup) |
|
181 { |
|
182 dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident())); |
|
183 if (!initialized) |
|
184 if (no_cleanup) |
|
185 _exit(0); |
|
186 else |
|
187 exit(0); |
|
188 _endthread(); |
|
189 } |
|
190 |
|
191 void |
|
192 PyThread_exit_thread(void) |
|
193 { |
|
194 do_PyThread_exit_thread(0); |
|
195 } |
|
196 |
|
197 void |
|
198 PyThread__exit_thread(void) |
|
199 { |
|
200 do_PyThread_exit_thread(1); |
|
201 } |
|
202 |
|
203 #ifndef NO_EXIT_PROG |
|
204 static void |
|
205 do_PyThread_exit_prog(int status, int no_cleanup) |
|
206 { |
|
207 dprintf(("PyThread_exit_prog(%d) called\n", status)); |
|
208 if (!initialized) |
|
209 if (no_cleanup) |
|
210 _exit(status); |
|
211 else |
|
212 exit(status); |
|
213 } |
|
214 |
|
215 void |
|
216 PyThread_exit_prog(int status) |
|
217 { |
|
218 do_PyThread_exit_prog(status, 0); |
|
219 } |
|
220 |
|
221 void |
|
222 PyThread__exit_prog(int status) |
|
223 { |
|
224 do_PyThread_exit_prog(status, 1); |
|
225 } |
|
226 #endif /* NO_EXIT_PROG */ |
|
227 |
|
228 /* |
|
229 * Lock support. It has too be implemented as semaphores. |
|
230 * I [Dag] tried to implement it with mutex but I could find a way to |
|
231 * tell whether a thread already own the lock or not. |
|
232 */ |
|
233 PyThread_type_lock |
|
234 PyThread_allocate_lock(void) |
|
235 { |
|
236 PNRMUTEX aLock; |
|
237 |
|
238 dprintf(("PyThread_allocate_lock called\n")); |
|
239 if (!initialized) |
|
240 PyThread_init_thread(); |
|
241 |
|
242 aLock = AllocNonRecursiveMutex() ; |
|
243 |
|
244 dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); |
|
245 |
|
246 return (PyThread_type_lock) aLock; |
|
247 } |
|
248 |
|
249 void |
|
250 PyThread_free_lock(PyThread_type_lock aLock) |
|
251 { |
|
252 dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); |
|
253 |
|
254 FreeNonRecursiveMutex(aLock) ; |
|
255 } |
|
256 |
|
257 /* |
|
258 * Return 1 on success if the lock was acquired |
|
259 * |
|
260 * and 0 if the lock was not acquired. This means a 0 is returned |
|
261 * if the lock has already been acquired by this thread! |
|
262 */ |
|
263 int |
|
264 PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) |
|
265 { |
|
266 int success ; |
|
267 |
|
268 dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag)); |
|
269 |
|
270 success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ; |
|
271 |
|
272 dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success)); |
|
273 |
|
274 return success; |
|
275 } |
|
276 |
|
277 void |
|
278 PyThread_release_lock(PyThread_type_lock aLock) |
|
279 { |
|
280 dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); |
|
281 |
|
282 if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) |
|
283 dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); |
|
284 } |
|
285 |
|
286 /* minimum/maximum thread stack sizes supported */ |
|
287 #define THREAD_MIN_STACKSIZE 0x8000 /* 32kB */ |
|
288 #define THREAD_MAX_STACKSIZE 0x10000000 /* 256MB */ |
|
289 |
|
290 /* set the thread stack size. |
|
291 * Return 0 if size is valid, -1 otherwise. |
|
292 */ |
|
293 static int |
|
294 _pythread_nt_set_stacksize(size_t size) |
|
295 { |
|
296 /* set to default */ |
|
297 if (size == 0) { |
|
298 _pythread_stacksize = 0; |
|
299 return 0; |
|
300 } |
|
301 |
|
302 /* valid range? */ |
|
303 if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) { |
|
304 _pythread_stacksize = size; |
|
305 return 0; |
|
306 } |
|
307 |
|
308 return -1; |
|
309 } |
|
310 |
|
311 #define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x) |