nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* GLIB - Library of useful routines for C programming |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
||
3 | * |
||
4 | * gthread.c: solaris thread system implementation |
||
5 | * Copyright 1998-2001 Sebastian Wilhelmi; University of Karlsruhe |
||
6 | * Copyright 2001 Hans Breuer |
||
7 | * |
||
8 | * This library is free software; you can redistribute it and/or |
||
9 | * modify it under the terms of the GNU Lesser General Public |
||
10 | * License as published by the Free Software Foundation; either |
||
11 | * version 2 of the License, or (at your option) any later version. |
||
12 | * |
||
13 | * This library is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
16 | * Lesser General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU Lesser General Public |
||
19 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
20 | */ |
||
21 | |||
22 | /* |
||
23 | * Modified by the GLib Team and others 1997-2000. See the AUTHORS |
||
24 | * file for a list of people on the GLib Team. See the ChangeLog |
||
25 | * files for a list of changes. These files are distributed with |
||
26 | * GLib at ftp://ftp.gtk.org/pub/gtk/. |
||
27 | */ |
||
28 | |||
29 | /* The GMutex and GCond implementations in this file are some of the |
||
30 | * lowest-level code in GLib. All other parts of GLib (messages, |
||
31 | * memory, slices, etc) assume that they can freely use these facilities |
||
32 | * without risking recursion. |
||
33 | * |
||
34 | * As such, these functions are NOT permitted to call any other part of |
||
35 | * GLib. |
||
36 | * |
||
37 | * The thread manipulation functions (create, exit, join, etc.) have |
||
38 | * more freedom -- they can do as they please. |
||
39 | */ |
||
40 | |||
41 | #include "config.h" |
||
42 | |||
43 | #include "glib.h" |
||
44 | #include "glib-init.h" |
||
45 | #include "gthread.h" |
||
46 | #include "gthreadprivate.h" |
||
47 | #include "gslice.h" |
||
48 | |||
49 | #include <windows.h> |
||
50 | |||
51 | #include <process.h> |
||
52 | #include <stdlib.h> |
||
53 | #include <stdio.h> |
||
54 | |||
55 | static void |
||
56 | g_thread_abort (gint status, |
||
57 | const gchar *function) |
||
58 | { |
||
59 | fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s. Aborting.\n", |
||
60 | strerror (status), function); |
||
61 | abort (); |
||
62 | } |
||
63 | |||
64 | /* Starting with Vista and Windows 2008, we have access to the |
||
65 | * CONDITION_VARIABLE and SRWLock primatives on Windows, which are |
||
66 | * pretty reasonable approximations of the primatives specified in |
||
67 | * POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively). |
||
68 | * |
||
69 | * Both of these types are structs containing a single pointer. That |
||
70 | * pointer is used as an atomic bitfield to support user-space mutexes |
||
71 | * that only get the kernel involved in cases of contention (similar |
||
72 | * to how futex()-based mutexes work on Linux). The biggest advantage |
||
73 | * of these new types is that they can be statically initialised to |
||
74 | * zero. That means that they are completely ABI compatible with our |
||
75 | * GMutex and GCond APIs. |
||
76 | * |
||
77 | * Unfortunately, Windows XP lacks these facilities and GLib still |
||
78 | * needs to support Windows XP. Our approach here is as follows: |
||
79 | * |
||
80 | * - avoid depending on structure declarations at compile-time by |
||
81 | * declaring our own GMutex and GCond strutures to be |
||
82 | * ABI-compatible with SRWLock and CONDITION_VARIABLE and using |
||
83 | * those instead |
||
84 | * |
||
85 | * - avoid a hard dependency on the symbols used to manipulate these |
||
86 | * structures by doing a dynamic lookup of those symbols at |
||
87 | * runtime |
||
88 | * |
||
89 | * - if the symbols are not available, emulate them using other |
||
90 | * primatives |
||
91 | * |
||
92 | * Using this approach also allows us to easily build a GLib that lacks |
||
93 | * support for Windows XP or to remove this code entirely when XP is no |
||
94 | * longer supported (end of line is currently April 8, 2014). |
||
95 | */ |
||
96 | typedef struct |
||
97 | { |
||
98 | void (__stdcall * CallThisOnThreadExit) (void); /* fake */ |
||
99 | |||
100 | void (__stdcall * InitializeSRWLock) (gpointer lock); |
||
101 | void (__stdcall * DeleteSRWLock) (gpointer lock); /* fake */ |
||
102 | void (__stdcall * AcquireSRWLockExclusive) (gpointer lock); |
||
103 | BOOLEAN (__stdcall * TryAcquireSRWLockExclusive) (gpointer lock); |
||
104 | void (__stdcall * ReleaseSRWLockExclusive) (gpointer lock); |
||
105 | void (__stdcall * AcquireSRWLockShared) (gpointer lock); |
||
106 | BOOLEAN (__stdcall * TryAcquireSRWLockShared) (gpointer lock); |
||
107 | void (__stdcall * ReleaseSRWLockShared) (gpointer lock); |
||
108 | |||
109 | void (__stdcall * InitializeConditionVariable) (gpointer cond); |
||
110 | void (__stdcall * DeleteConditionVariable) (gpointer cond); /* fake */ |
||
111 | BOOL (__stdcall * SleepConditionVariableSRW) (gpointer cond, |
||
112 | gpointer lock, |
||
113 | DWORD timeout, |
||
114 | ULONG flags); |
||
115 | void (__stdcall * WakeAllConditionVariable) (gpointer cond); |
||
116 | void (__stdcall * WakeConditionVariable) (gpointer cond); |
||
117 | } GThreadImplVtable; |
||
118 | |||
119 | static GThreadImplVtable g_thread_impl_vtable; |
||
120 | |||
121 | /* {{{1 GMutex */ |
||
122 | void |
||
123 | g_mutex_init (GMutex *mutex) |
||
124 | { |
||
125 | g_thread_impl_vtable.InitializeSRWLock (mutex); |
||
126 | } |
||
127 | |||
128 | void |
||
129 | g_mutex_clear (GMutex *mutex) |
||
130 | { |
||
131 | if (g_thread_impl_vtable.DeleteSRWLock != NULL) |
||
132 | g_thread_impl_vtable.DeleteSRWLock (mutex); |
||
133 | } |
||
134 | |||
135 | void |
||
136 | g_mutex_lock (GMutex *mutex) |
||
137 | { |
||
138 | g_thread_impl_vtable.AcquireSRWLockExclusive (mutex); |
||
139 | } |
||
140 | |||
141 | gboolean |
||
142 | g_mutex_trylock (GMutex *mutex) |
||
143 | { |
||
144 | return g_thread_impl_vtable.TryAcquireSRWLockExclusive (mutex); |
||
145 | } |
||
146 | |||
147 | void |
||
148 | g_mutex_unlock (GMutex *mutex) |
||
149 | { |
||
150 | g_thread_impl_vtable.ReleaseSRWLockExclusive (mutex); |
||
151 | } |
||
152 | |||
153 | /* {{{1 GRecMutex */ |
||
154 | |||
155 | static CRITICAL_SECTION * |
||
156 | g_rec_mutex_impl_new (void) |
||
157 | { |
||
158 | CRITICAL_SECTION *cs; |
||
159 | |||
160 | cs = g_slice_new (CRITICAL_SECTION); |
||
161 | InitializeCriticalSection (cs); |
||
162 | |||
163 | return cs; |
||
164 | } |
||
165 | |||
166 | static void |
||
167 | g_rec_mutex_impl_free (CRITICAL_SECTION *cs) |
||
168 | { |
||
169 | DeleteCriticalSection (cs); |
||
170 | g_slice_free (CRITICAL_SECTION, cs); |
||
171 | } |
||
172 | |||
173 | static CRITICAL_SECTION * |
||
174 | g_rec_mutex_get_impl (GRecMutex *mutex) |
||
175 | { |
||
176 | CRITICAL_SECTION *impl = mutex->p; |
||
177 | |||
178 | if G_UNLIKELY (mutex->p == NULL) |
||
179 | { |
||
180 | impl = g_rec_mutex_impl_new (); |
||
181 | if (InterlockedCompareExchangePointer (&mutex->p, impl, NULL) != NULL) |
||
182 | g_rec_mutex_impl_free (impl); |
||
183 | impl = mutex->p; |
||
184 | } |
||
185 | |||
186 | return impl; |
||
187 | } |
||
188 | |||
189 | void |
||
190 | g_rec_mutex_init (GRecMutex *mutex) |
||
191 | { |
||
192 | mutex->p = g_rec_mutex_impl_new (); |
||
193 | } |
||
194 | |||
195 | void |
||
196 | g_rec_mutex_clear (GRecMutex *mutex) |
||
197 | { |
||
198 | g_rec_mutex_impl_free (mutex->p); |
||
199 | } |
||
200 | |||
201 | void |
||
202 | g_rec_mutex_lock (GRecMutex *mutex) |
||
203 | { |
||
204 | EnterCriticalSection (g_rec_mutex_get_impl (mutex)); |
||
205 | } |
||
206 | |||
207 | void |
||
208 | g_rec_mutex_unlock (GRecMutex *mutex) |
||
209 | { |
||
210 | LeaveCriticalSection (mutex->p); |
||
211 | } |
||
212 | |||
213 | gboolean |
||
214 | g_rec_mutex_trylock (GRecMutex *mutex) |
||
215 | { |
||
216 | return TryEnterCriticalSection (g_rec_mutex_get_impl (mutex)); |
||
217 | } |
||
218 | |||
219 | /* {{{1 GRWLock */ |
||
220 | |||
221 | void |
||
222 | g_rw_lock_init (GRWLock *lock) |
||
223 | { |
||
224 | g_thread_impl_vtable.InitializeSRWLock (lock); |
||
225 | } |
||
226 | |||
227 | void |
||
228 | g_rw_lock_clear (GRWLock *lock) |
||
229 | { |
||
230 | if (g_thread_impl_vtable.DeleteSRWLock != NULL) |
||
231 | g_thread_impl_vtable.DeleteSRWLock (lock); |
||
232 | } |
||
233 | |||
234 | void |
||
235 | g_rw_lock_writer_lock (GRWLock *lock) |
||
236 | { |
||
237 | g_thread_impl_vtable.AcquireSRWLockExclusive (lock); |
||
238 | } |
||
239 | |||
240 | gboolean |
||
241 | g_rw_lock_writer_trylock (GRWLock *lock) |
||
242 | { |
||
243 | return g_thread_impl_vtable.TryAcquireSRWLockExclusive (lock); |
||
244 | } |
||
245 | |||
246 | void |
||
247 | g_rw_lock_writer_unlock (GRWLock *lock) |
||
248 | { |
||
249 | g_thread_impl_vtable.ReleaseSRWLockExclusive (lock); |
||
250 | } |
||
251 | |||
252 | void |
||
253 | g_rw_lock_reader_lock (GRWLock *lock) |
||
254 | { |
||
255 | g_thread_impl_vtable.AcquireSRWLockShared (lock); |
||
256 | } |
||
257 | |||
258 | gboolean |
||
259 | g_rw_lock_reader_trylock (GRWLock *lock) |
||
260 | { |
||
261 | return g_thread_impl_vtable.TryAcquireSRWLockShared (lock); |
||
262 | } |
||
263 | |||
264 | void |
||
265 | g_rw_lock_reader_unlock (GRWLock *lock) |
||
266 | { |
||
267 | g_thread_impl_vtable.ReleaseSRWLockShared (lock); |
||
268 | } |
||
269 | |||
270 | /* {{{1 GCond */ |
||
271 | void |
||
272 | g_cond_init (GCond *cond) |
||
273 | { |
||
274 | g_thread_impl_vtable.InitializeConditionVariable (cond); |
||
275 | } |
||
276 | |||
277 | void |
||
278 | g_cond_clear (GCond *cond) |
||
279 | { |
||
280 | if (g_thread_impl_vtable.DeleteConditionVariable) |
||
281 | g_thread_impl_vtable.DeleteConditionVariable (cond); |
||
282 | } |
||
283 | |||
284 | void |
||
285 | g_cond_signal (GCond *cond) |
||
286 | { |
||
287 | g_thread_impl_vtable.WakeConditionVariable (cond); |
||
288 | } |
||
289 | |||
290 | void |
||
291 | g_cond_broadcast (GCond *cond) |
||
292 | { |
||
293 | g_thread_impl_vtable.WakeAllConditionVariable (cond); |
||
294 | } |
||
295 | |||
296 | void |
||
297 | g_cond_wait (GCond *cond, |
||
298 | GMutex *entered_mutex) |
||
299 | { |
||
300 | g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0); |
||
301 | } |
||
302 | |||
303 | gboolean |
||
304 | g_cond_wait_until (GCond *cond, |
||
305 | GMutex *entered_mutex, |
||
306 | gint64 end_time) |
||
307 | { |
||
308 | gint64 span; |
||
309 | |||
310 | span = end_time - g_get_monotonic_time (); |
||
311 | |||
312 | if G_UNLIKELY (span < 0) |
||
313 | span = 0; |
||
314 | |||
315 | if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * G_MAXINT32) |
||
316 | span = INFINITE; |
||
317 | |||
318 | return g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span / 1000, 0); |
||
319 | } |
||
320 | |||
321 | /* {{{1 GPrivate */ |
||
322 | |||
323 | typedef struct _GPrivateDestructor GPrivateDestructor; |
||
324 | |||
325 | struct _GPrivateDestructor |
||
326 | { |
||
327 | DWORD index; |
||
328 | GDestroyNotify notify; |
||
329 | GPrivateDestructor *next; |
||
330 | }; |
||
331 | |||
332 | static GPrivateDestructor * volatile g_private_destructors; |
||
333 | static CRITICAL_SECTION g_private_lock; |
||
334 | |||
335 | static DWORD |
||
336 | g_private_get_impl (GPrivate *key) |
||
337 | { |
||
338 | DWORD impl = (DWORD) key->p; |
||
339 | |||
340 | if G_UNLIKELY (impl == 0) |
||
341 | { |
||
342 | EnterCriticalSection (&g_private_lock); |
||
343 | impl = (DWORD) key->p; |
||
344 | if (impl == 0) |
||
345 | { |
||
346 | GPrivateDestructor *destructor; |
||
347 | |||
348 | impl = TlsAlloc (); |
||
349 | |||
350 | if (impl == TLS_OUT_OF_INDEXES) |
||
351 | g_thread_abort (0, "TlsAlloc"); |
||
352 | |||
353 | if (key->notify != NULL) |
||
354 | { |
||
355 | destructor = malloc (sizeof (GPrivateDestructor)); |
||
356 | if G_UNLIKELY (destructor == NULL) |
||
357 | g_thread_abort (errno, "malloc"); |
||
358 | destructor->index = impl; |
||
359 | destructor->notify = key->notify; |
||
360 | destructor->next = g_private_destructors; |
||
361 | |||
362 | /* We need to do an atomic store due to the unlocked |
||
363 | * access to the destructor list from the thread exit |
||
364 | * function. |
||
365 | * |
||
366 | * It can double as a sanity check... |
||
367 | */ |
||
368 | if (InterlockedCompareExchangePointer (&g_private_destructors, destructor, |
||
369 | destructor->next) != destructor->next) |
||
370 | g_thread_abort (0, "g_private_get_impl(1)"); |
||
371 | } |
||
372 | |||
373 | /* Ditto, due to the unlocked access on the fast path */ |
||
374 | if (InterlockedCompareExchangePointer (&key->p, impl, NULL) != NULL) |
||
375 | g_thread_abort (0, "g_private_get_impl(2)"); |
||
376 | } |
||
377 | LeaveCriticalSection (&g_private_lock); |
||
378 | } |
||
379 | |||
380 | return impl; |
||
381 | } |
||
382 | |||
383 | gpointer |
||
384 | g_private_get (GPrivate *key) |
||
385 | { |
||
386 | return TlsGetValue (g_private_get_impl (key)); |
||
387 | } |
||
388 | |||
389 | void |
||
390 | g_private_set (GPrivate *key, |
||
391 | gpointer value) |
||
392 | { |
||
393 | TlsSetValue (g_private_get_impl (key), value); |
||
394 | } |
||
395 | |||
396 | void |
||
397 | g_private_replace (GPrivate *key, |
||
398 | gpointer value) |
||
399 | { |
||
400 | DWORD impl = g_private_get_impl (key); |
||
401 | gpointer old; |
||
402 | |||
403 | old = TlsGetValue (impl); |
||
404 | if (old && key->notify) |
||
405 | key->notify (old); |
||
406 | TlsSetValue (impl, value); |
||
407 | } |
||
408 | |||
409 | /* {{{1 GThread */ |
||
410 | |||
411 | #define win32_check_for_error(what) G_STMT_START{ \ |
||
412 | if (!(what)) \ |
||
413 | g_error ("file %s: line %d (%s): error %s during %s", \ |
||
414 | __FILE__, __LINE__, G_STRFUNC, \ |
||
415 | g_win32_error_message (GetLastError ()), #what); \ |
||
416 | }G_STMT_END |
||
417 | |||
418 | #define G_MUTEX_SIZE (sizeof (gpointer)) |
||
419 | |||
420 | typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *); |
||
421 | |||
422 | typedef struct |
||
423 | { |
||
424 | GRealThread thread; |
||
425 | |||
426 | GThreadFunc proxy; |
||
427 | HANDLE handle; |
||
428 | } GThreadWin32; |
||
429 | |||
430 | void |
||
431 | g_system_thread_free (GRealThread *thread) |
||
432 | { |
||
433 | GThreadWin32 *wt = (GThreadWin32 *) thread; |
||
434 | |||
435 | win32_check_for_error (CloseHandle (wt->handle)); |
||
436 | g_slice_free (GThreadWin32, wt); |
||
437 | } |
||
438 | |||
439 | void |
||
440 | g_system_thread_exit (void) |
||
441 | { |
||
442 | _endthreadex (0); |
||
443 | } |
||
444 | |||
445 | static guint __stdcall |
||
446 | g_thread_win32_proxy (gpointer data) |
||
447 | { |
||
448 | GThreadWin32 *self = data; |
||
449 | |||
450 | self->proxy (self); |
||
451 | |||
452 | g_system_thread_exit (); |
||
453 | |||
454 | g_assert_not_reached (); |
||
455 | |||
456 | return 0; |
||
457 | } |
||
458 | |||
459 | GRealThread * |
||
460 | g_system_thread_new (GThreadFunc func, |
||
461 | gulong stack_size, |
||
462 | GError **error) |
||
463 | { |
||
464 | GThreadWin32 *thread; |
||
465 | guint ignore; |
||
466 | |||
467 | thread = g_slice_new0 (GThreadWin32); |
||
468 | thread->proxy = func; |
||
469 | |||
470 | thread->handle = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_win32_proxy, thread, 0, &ignore); |
||
471 | |||
472 | if (thread->handle == NULL) |
||
473 | { |
||
474 | gchar *win_error = g_win32_error_message (GetLastError ()); |
||
475 | g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, |
||
476 | "Error creating thread: %s", win_error); |
||
477 | g_free (win_error); |
||
478 | g_slice_free (GThreadWin32, thread); |
||
479 | return NULL; |
||
480 | } |
||
481 | |||
482 | return (GRealThread *) thread; |
||
483 | } |
||
484 | |||
485 | void |
||
486 | g_thread_yield (void) |
||
487 | { |
||
488 | Sleep(0); |
||
489 | } |
||
490 | |||
491 | void |
||
492 | g_system_thread_wait (GRealThread *thread) |
||
493 | { |
||
494 | GThreadWin32 *wt = (GThreadWin32 *) thread; |
||
495 | |||
496 | win32_check_for_error (WAIT_FAILED != WaitForSingleObject (wt->handle, INFINITE)); |
||
497 | } |
||
498 | |||
499 | #define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388) |
||
500 | |||
501 | #ifndef _MSC_VER |
||
502 | static void *SetThreadName_VEH_handle = NULL; |
||
503 | |||
504 | static LONG __stdcall |
||
505 | SetThreadName_VEH (PEXCEPTION_POINTERS ExceptionInfo) |
||
506 | { |
||
507 | if (ExceptionInfo->ExceptionRecord != NULL && |
||
508 | ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME) |
||
509 | return EXCEPTION_CONTINUE_EXECUTION; |
||
510 | |||
511 | return EXCEPTION_CONTINUE_SEARCH; |
||
512 | } |
||
513 | #endif |
||
514 | |||
515 | typedef struct _THREADNAME_INFO |
||
516 | { |
||
517 | DWORD dwType; /* must be 0x1000 */ |
||
518 | LPCSTR szName; /* pointer to name (in user addr space) */ |
||
519 | DWORD dwThreadID; /* thread ID (-1=caller thread) */ |
||
520 | DWORD dwFlags; /* reserved for future use, must be zero */ |
||
521 | } THREADNAME_INFO; |
||
522 | |||
523 | static void |
||
524 | SetThreadName (DWORD dwThreadID, |
||
525 | LPCSTR szThreadName) |
||
526 | { |
||
527 | THREADNAME_INFO info; |
||
528 | DWORD infosize; |
||
529 | |||
530 | info.dwType = 0x1000; |
||
531 | info.szName = szThreadName; |
||
532 | info.dwThreadID = dwThreadID; |
||
533 | info.dwFlags = 0; |
||
534 | |||
535 | infosize = sizeof (info) / sizeof (DWORD); |
||
536 | |||
537 | #ifdef _MSC_VER |
||
538 | __try |
||
539 | { |
||
540 | RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (DWORD *) &info); |
||
541 | } |
||
542 | __except (EXCEPTION_EXECUTE_HANDLER) |
||
543 | { |
||
544 | } |
||
545 | #else |
||
546 | /* Without a debugger we *must* have an exception handler, |
||
547 | * otherwise raising an exception will crash the process. |
||
548 | */ |
||
549 | if ((!IsDebuggerPresent ()) && (SetThreadName_VEH_handle == NULL)) |
||
550 | return; |
||
551 | |||
552 | RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (DWORD *) &info); |
||
553 | #endif |
||
554 | } |
||
555 | |||
556 | void |
||
557 | g_system_thread_set_name (const gchar *name) |
||
558 | { |
||
559 | SetThreadName ((DWORD) -1, name); |
||
560 | } |
||
561 | |||
562 | /* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */ |
||
563 | |||
564 | static CRITICAL_SECTION g_thread_xp_lock; |
||
565 | static DWORD g_thread_xp_waiter_tls; |
||
566 | |||
567 | /* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */ |
||
568 | typedef struct _GThreadXpWaiter GThreadXpWaiter; |
||
569 | struct _GThreadXpWaiter |
||
570 | { |
||
571 | HANDLE event; |
||
572 | volatile GThreadXpWaiter *next; |
||
573 | volatile GThreadXpWaiter **my_owner; |
||
574 | }; |
||
575 | |||
576 | static GThreadXpWaiter * |
||
577 | g_thread_xp_waiter_get (void) |
||
578 | { |
||
579 | GThreadXpWaiter *waiter; |
||
580 | |||
581 | waiter = TlsGetValue (g_thread_xp_waiter_tls); |
||
582 | |||
583 | if G_UNLIKELY (waiter == NULL) |
||
584 | { |
||
585 | waiter = malloc (sizeof (GThreadXpWaiter)); |
||
586 | if (waiter == NULL) |
||
587 | g_thread_abort (GetLastError (), "malloc"); |
||
588 | waiter->event = CreateEvent (0, FALSE, FALSE, NULL); |
||
589 | if (waiter->event == NULL) |
||
590 | g_thread_abort (GetLastError (), "CreateEvent"); |
||
591 | waiter->my_owner = NULL; |
||
592 | |||
593 | TlsSetValue (g_thread_xp_waiter_tls, waiter); |
||
594 | } |
||
595 | |||
596 | return waiter; |
||
597 | } |
||
598 | |||
599 | static void __stdcall |
||
600 | g_thread_xp_CallThisOnThreadExit (void) |
||
601 | { |
||
602 | GThreadXpWaiter *waiter; |
||
603 | |||
604 | waiter = TlsGetValue (g_thread_xp_waiter_tls); |
||
605 | |||
606 | if (waiter != NULL) |
||
607 | { |
||
608 | TlsSetValue (g_thread_xp_waiter_tls, NULL); |
||
609 | CloseHandle (waiter->event); |
||
610 | free (waiter); |
||
611 | } |
||
612 | } |
||
613 | |||
614 | /* {{{2 SRWLock emulation */ |
||
615 | typedef struct |
||
616 | { |
||
617 | CRITICAL_SECTION writer_lock; |
||
618 | gboolean ever_shared; /* protected by writer_lock */ |
||
619 | gboolean writer_locked; /* protected by writer_lock */ |
||
620 | |||
621 | /* below is only ever touched if ever_shared becomes true */ |
||
622 | CRITICAL_SECTION atomicity; |
||
623 | GThreadXpWaiter *queued_writer; /* protected by atomicity lock */ |
||
624 | gint num_readers; /* protected by atomicity lock */ |
||
625 | } GThreadSRWLock; |
||
626 | |||
627 | static void __stdcall |
||
628 | g_thread_xp_InitializeSRWLock (gpointer mutex) |
||
629 | { |
||
630 | *(GThreadSRWLock * volatile *) mutex = NULL; |
||
631 | } |
||
632 | |||
633 | static void __stdcall |
||
634 | g_thread_xp_DeleteSRWLock (gpointer mutex) |
||
635 | { |
||
636 | GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; |
||
637 | |||
638 | if (lock) |
||
639 | { |
||
640 | if (lock->ever_shared) |
||
641 | DeleteCriticalSection (&lock->atomicity); |
||
642 | |||
643 | DeleteCriticalSection (&lock->writer_lock); |
||
644 | free (lock); |
||
645 | } |
||
646 | } |
||
647 | |||
648 | static GThreadSRWLock * __stdcall |
||
649 | g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock) |
||
650 | { |
||
651 | GThreadSRWLock *result; |
||
652 | |||
653 | /* It looks like we're missing some barriers here, but this code only |
||
654 | * ever runs on Windows XP, which in turn only ever runs on hardware |
||
655 | * with a relatively rigid memory model. The 'volatile' will take |
||
656 | * care of the compiler. |
||
657 | */ |
||
658 | result = *lock; |
||
659 | |||
660 | if G_UNLIKELY (result == NULL) |
||
661 | { |
||
662 | EnterCriticalSection (&g_thread_xp_lock); |
||
663 | |||
664 | /* Check again */ |
||
665 | result = *lock; |
||
666 | if (result == NULL) |
||
667 | { |
||
668 | result = malloc (sizeof (GThreadSRWLock)); |
||
669 | |||
670 | if (result == NULL) |
||
671 | g_thread_abort (errno, "malloc"); |
||
672 | |||
673 | InitializeCriticalSection (&result->writer_lock); |
||
674 | result->writer_locked = FALSE; |
||
675 | result->ever_shared = FALSE; |
||
676 | *lock = result; |
||
677 | } |
||
678 | |||
679 | LeaveCriticalSection (&g_thread_xp_lock); |
||
680 | } |
||
681 | |||
682 | return result; |
||
683 | } |
||
684 | |||
685 | static void __stdcall |
||
686 | g_thread_xp_AcquireSRWLockExclusive (gpointer mutex) |
||
687 | { |
||
688 | GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); |
||
689 | |||
690 | EnterCriticalSection (&lock->writer_lock); |
||
691 | |||
692 | /* CRITICAL_SECTION is reentrant, but SRWLock is not. |
||
693 | * Detect the deadlock that would occur on later Windows version. |
||
694 | */ |
||
695 | g_assert (!lock->writer_locked); |
||
696 | lock->writer_locked = TRUE; |
||
697 | |||
698 | if (lock->ever_shared) |
||
699 | { |
||
700 | GThreadXpWaiter *waiter = NULL; |
||
701 | |||
702 | EnterCriticalSection (&lock->atomicity); |
||
703 | if (lock->num_readers > 0) |
||
704 | lock->queued_writer = waiter = g_thread_xp_waiter_get (); |
||
705 | LeaveCriticalSection (&lock->atomicity); |
||
706 | |||
707 | if (waiter != NULL) |
||
708 | WaitForSingleObject (waiter->event, INFINITE); |
||
709 | |||
710 | lock->queued_writer = NULL; |
||
711 | } |
||
712 | } |
||
713 | |||
714 | static BOOLEAN __stdcall |
||
715 | g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex) |
||
716 | { |
||
717 | GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); |
||
718 | |||
719 | if (!TryEnterCriticalSection (&lock->writer_lock)) |
||
720 | return FALSE; |
||
721 | |||
722 | /* CRITICAL_SECTION is reentrant, but SRWLock is not. |
||
723 | * Ensure that this properly returns FALSE (as SRWLock would). |
||
724 | */ |
||
725 | if G_UNLIKELY (lock->writer_locked) |
||
726 | { |
||
727 | LeaveCriticalSection (&lock->writer_lock); |
||
728 | return FALSE; |
||
729 | } |
||
730 | |||
731 | lock->writer_locked = TRUE; |
||
732 | |||
733 | if (lock->ever_shared) |
||
734 | { |
||
735 | gboolean available; |
||
736 | |||
737 | EnterCriticalSection (&lock->atomicity); |
||
738 | available = lock->num_readers == 0; |
||
739 | LeaveCriticalSection (&lock->atomicity); |
||
740 | |||
741 | if (!available) |
||
742 | { |
||
743 | LeaveCriticalSection (&lock->writer_lock); |
||
744 | return FALSE; |
||
745 | } |
||
746 | } |
||
747 | |||
748 | return TRUE; |
||
749 | } |
||
750 | |||
751 | static void __stdcall |
||
752 | g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex) |
||
753 | { |
||
754 | GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; |
||
755 | |||
756 | lock->writer_locked = FALSE; |
||
757 | |||
758 | /* We need this until we fix some weird parts of GLib that try to |
||
759 | * unlock freshly-allocated mutexes. |
||
760 | */ |
||
761 | if (lock != NULL) |
||
762 | LeaveCriticalSection (&lock->writer_lock); |
||
763 | } |
||
764 | |||
765 | static void |
||
766 | g_thread_xp_srwlock_become_reader (GThreadSRWLock *lock) |
||
767 | { |
||
768 | if G_UNLIKELY (!lock->ever_shared) |
||
769 | { |
||
770 | InitializeCriticalSection (&lock->atomicity); |
||
771 | lock->queued_writer = NULL; |
||
772 | lock->num_readers = 0; |
||
773 | |||
774 | lock->ever_shared = TRUE; |
||
775 | } |
||
776 | |||
777 | EnterCriticalSection (&lock->atomicity); |
||
778 | lock->num_readers++; |
||
779 | LeaveCriticalSection (&lock->atomicity); |
||
780 | } |
||
781 | |||
782 | static void __stdcall |
||
783 | g_thread_xp_AcquireSRWLockShared (gpointer mutex) |
||
784 | { |
||
785 | GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); |
||
786 | |||
787 | EnterCriticalSection (&lock->writer_lock); |
||
788 | |||
789 | /* See g_thread_xp_AcquireSRWLockExclusive */ |
||
790 | g_assert (!lock->writer_locked); |
||
791 | |||
792 | g_thread_xp_srwlock_become_reader (lock); |
||
793 | |||
794 | LeaveCriticalSection (&lock->writer_lock); |
||
795 | } |
||
796 | |||
797 | static BOOLEAN __stdcall |
||
798 | g_thread_xp_TryAcquireSRWLockShared (gpointer mutex) |
||
799 | { |
||
800 | GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); |
||
801 | |||
802 | if (!TryEnterCriticalSection (&lock->writer_lock)) |
||
803 | return FALSE; |
||
804 | |||
805 | /* See g_thread_xp_AcquireSRWLockExclusive */ |
||
806 | if G_UNLIKELY (lock->writer_locked) |
||
807 | { |
||
808 | LeaveCriticalSection (&lock->writer_lock); |
||
809 | return FALSE; |
||
810 | } |
||
811 | |||
812 | g_thread_xp_srwlock_become_reader (lock); |
||
813 | |||
814 | LeaveCriticalSection (&lock->writer_lock); |
||
815 | |||
816 | return TRUE; |
||
817 | } |
||
818 | |||
819 | static void __stdcall |
||
820 | g_thread_xp_ReleaseSRWLockShared (gpointer mutex) |
||
821 | { |
||
822 | GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); |
||
823 | |||
824 | EnterCriticalSection (&lock->atomicity); |
||
825 | |||
826 | lock->num_readers--; |
||
827 | |||
828 | if (lock->num_readers == 0 && lock->queued_writer) |
||
829 | SetEvent (lock->queued_writer->event); |
||
830 | |||
831 | LeaveCriticalSection (&lock->atomicity); |
||
832 | } |
||
833 | |||
834 | /* {{{2 CONDITION_VARIABLE emulation */ |
||
835 | typedef struct |
||
836 | { |
||
837 | volatile GThreadXpWaiter *first; |
||
838 | volatile GThreadXpWaiter **last_ptr; |
||
839 | } GThreadXpCONDITION_VARIABLE; |
||
840 | |||
841 | static void __stdcall |
||
842 | g_thread_xp_InitializeConditionVariable (gpointer cond) |
||
843 | { |
||
844 | *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL; |
||
845 | } |
||
846 | |||
847 | static void __stdcall |
||
848 | g_thread_xp_DeleteConditionVariable (gpointer cond) |
||
849 | { |
||
850 | GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond; |
||
851 | |||
852 | if (cv) |
||
853 | free (cv); |
||
854 | } |
||
855 | |||
856 | static GThreadXpCONDITION_VARIABLE * __stdcall |
||
857 | g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond) |
||
858 | { |
||
859 | GThreadXpCONDITION_VARIABLE *result; |
||
860 | |||
861 | /* It looks like we're missing some barriers here, but this code only |
||
862 | * ever runs on Windows XP, which in turn only ever runs on hardware |
||
863 | * with a relatively rigid memory model. The 'volatile' will take |
||
864 | * care of the compiler. |
||
865 | */ |
||
866 | result = *cond; |
||
867 | |||
868 | if G_UNLIKELY (result == NULL) |
||
869 | { |
||
870 | result = malloc (sizeof (GThreadXpCONDITION_VARIABLE)); |
||
871 | |||
872 | if (result == NULL) |
||
873 | g_thread_abort (errno, "malloc"); |
||
874 | |||
875 | result->first = NULL; |
||
876 | result->last_ptr = &result->first; |
||
877 | |||
878 | if (InterlockedCompareExchangePointer (cond, result, NULL) != NULL) |
||
879 | { |
||
880 | free (result); |
||
881 | result = *cond; |
||
882 | } |
||
883 | } |
||
884 | |||
885 | return result; |
||
886 | } |
||
887 | |||
888 | static BOOL __stdcall |
||
889 | g_thread_xp_SleepConditionVariableSRW (gpointer cond, |
||
890 | gpointer mutex, |
||
891 | DWORD timeout, |
||
892 | ULONG flags) |
||
893 | { |
||
894 | GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); |
||
895 | GThreadXpWaiter *waiter = g_thread_xp_waiter_get (); |
||
896 | DWORD status; |
||
897 | |||
898 | waiter->next = NULL; |
||
899 | |||
900 | EnterCriticalSection (&g_thread_xp_lock); |
||
901 | waiter->my_owner = cv->last_ptr; |
||
902 | *cv->last_ptr = waiter; |
||
903 | cv->last_ptr = &waiter->next; |
||
904 | LeaveCriticalSection (&g_thread_xp_lock); |
||
905 | |||
906 | g_mutex_unlock (mutex); |
||
907 | status = WaitForSingleObject (waiter->event, timeout); |
||
908 | |||
909 | if (status != WAIT_TIMEOUT && status != WAIT_OBJECT_0) |
||
910 | g_thread_abort (GetLastError (), "WaitForSingleObject"); |
||
911 | g_mutex_lock (mutex); |
||
912 | |||
913 | if (status == WAIT_TIMEOUT) |
||
914 | { |
||
915 | EnterCriticalSection (&g_thread_xp_lock); |
||
916 | if (waiter->my_owner) |
||
917 | { |
||
918 | if (waiter->next) |
||
919 | waiter->next->my_owner = waiter->my_owner; |
||
920 | else |
||
921 | cv->last_ptr = waiter->my_owner; |
||
922 | *waiter->my_owner = waiter->next; |
||
923 | waiter->my_owner = NULL; |
||
924 | } |
||
925 | LeaveCriticalSection (&g_thread_xp_lock); |
||
926 | } |
||
927 | |||
928 | return status == WAIT_OBJECT_0; |
||
929 | } |
||
930 | |||
931 | static void __stdcall |
||
932 | g_thread_xp_WakeConditionVariable (gpointer cond) |
||
933 | { |
||
934 | GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); |
||
935 | volatile GThreadXpWaiter *waiter; |
||
936 | |||
937 | EnterCriticalSection (&g_thread_xp_lock); |
||
938 | |||
939 | waiter = cv->first; |
||
940 | if (waiter != NULL) |
||
941 | { |
||
942 | waiter->my_owner = NULL; |
||
943 | cv->first = waiter->next; |
||
944 | if (cv->first != NULL) |
||
945 | cv->first->my_owner = &cv->first; |
||
946 | else |
||
947 | cv->last_ptr = &cv->first; |
||
948 | } |
||
949 | |||
950 | if (waiter != NULL) |
||
951 | SetEvent (waiter->event); |
||
952 | |||
953 | LeaveCriticalSection (&g_thread_xp_lock); |
||
954 | } |
||
955 | |||
956 | static void __stdcall |
||
957 | g_thread_xp_WakeAllConditionVariable (gpointer cond) |
||
958 | { |
||
959 | GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); |
||
960 | volatile GThreadXpWaiter *waiter; |
||
961 | |||
962 | EnterCriticalSection (&g_thread_xp_lock); |
||
963 | |||
964 | waiter = cv->first; |
||
965 | cv->first = NULL; |
||
966 | cv->last_ptr = &cv->first; |
||
967 | |||
968 | while (waiter != NULL) |
||
969 | { |
||
970 | volatile GThreadXpWaiter *next; |
||
971 | |||
972 | next = waiter->next; |
||
973 | SetEvent (waiter->event); |
||
974 | waiter->my_owner = NULL; |
||
975 | waiter = next; |
||
976 | } |
||
977 | |||
978 | LeaveCriticalSection (&g_thread_xp_lock); |
||
979 | } |
||
980 | |||
981 | /* {{{2 XP Setup */ |
||
982 | static void |
||
983 | g_thread_xp_init (void) |
||
984 | { |
||
985 | static const GThreadImplVtable g_thread_xp_impl_vtable = { |
||
986 | g_thread_xp_CallThisOnThreadExit, |
||
987 | g_thread_xp_InitializeSRWLock, |
||
988 | g_thread_xp_DeleteSRWLock, |
||
989 | g_thread_xp_AcquireSRWLockExclusive, |
||
990 | g_thread_xp_TryAcquireSRWLockExclusive, |
||
991 | g_thread_xp_ReleaseSRWLockExclusive, |
||
992 | g_thread_xp_AcquireSRWLockShared, |
||
993 | g_thread_xp_TryAcquireSRWLockShared, |
||
994 | g_thread_xp_ReleaseSRWLockShared, |
||
995 | g_thread_xp_InitializeConditionVariable, |
||
996 | g_thread_xp_DeleteConditionVariable, |
||
997 | g_thread_xp_SleepConditionVariableSRW, |
||
998 | g_thread_xp_WakeAllConditionVariable, |
||
999 | g_thread_xp_WakeConditionVariable |
||
1000 | }; |
||
1001 | |||
1002 | InitializeCriticalSection (&g_thread_xp_lock); |
||
1003 | g_thread_xp_waiter_tls = TlsAlloc (); |
||
1004 | |||
1005 | g_thread_impl_vtable = g_thread_xp_impl_vtable; |
||
1006 | } |
||
1007 | |||
1008 | /* {{{1 Epilogue */ |
||
1009 | |||
1010 | static gboolean |
||
1011 | g_thread_lookup_native_funcs (void) |
||
1012 | { |
||
1013 | GThreadImplVtable native_vtable = { 0, }; |
||
1014 | HMODULE kernel32; |
||
1015 | |||
1016 | kernel32 = GetModuleHandle ("KERNEL32.DLL"); |
||
1017 | |||
1018 | if (kernel32 == NULL) |
||
1019 | return FALSE; |
||
1020 | |||
1021 | #define GET_FUNC(name) if ((native_vtable.name = (void *) GetProcAddress (kernel32, #name)) == NULL) return FALSE |
||
1022 | GET_FUNC(InitializeSRWLock); |
||
1023 | GET_FUNC(AcquireSRWLockExclusive); |
||
1024 | GET_FUNC(TryAcquireSRWLockExclusive); |
||
1025 | GET_FUNC(ReleaseSRWLockExclusive); |
||
1026 | GET_FUNC(AcquireSRWLockShared); |
||
1027 | GET_FUNC(TryAcquireSRWLockShared); |
||
1028 | GET_FUNC(ReleaseSRWLockShared); |
||
1029 | |||
1030 | GET_FUNC(InitializeConditionVariable); |
||
1031 | GET_FUNC(SleepConditionVariableSRW); |
||
1032 | GET_FUNC(WakeAllConditionVariable); |
||
1033 | GET_FUNC(WakeConditionVariable); |
||
1034 | #undef GET_FUNC |
||
1035 | |||
1036 | g_thread_impl_vtable = native_vtable; |
||
1037 | |||
1038 | return TRUE; |
||
1039 | } |
||
1040 | |||
1041 | void |
||
1042 | g_thread_win32_init (void) |
||
1043 | { |
||
1044 | if (!g_thread_lookup_native_funcs ()) |
||
1045 | g_thread_xp_init (); |
||
1046 | |||
1047 | InitializeCriticalSection (&g_private_lock); |
||
1048 | |||
1049 | #ifndef _MSC_VER |
||
1050 | SetThreadName_VEH_handle = AddVectoredExceptionHandler (1, &SetThreadName_VEH); |
||
1051 | if (SetThreadName_VEH_handle == NULL) |
||
1052 | { |
||
1053 | /* This is bad, but what can we do? */ |
||
1054 | } |
||
1055 | #endif |
||
1056 | } |
||
1057 | |||
1058 | void |
||
1059 | g_thread_win32_thread_detach (void) |
||
1060 | { |
||
1061 | gboolean dtors_called; |
||
1062 | |||
1063 | do |
||
1064 | { |
||
1065 | GPrivateDestructor *dtor; |
||
1066 | |||
1067 | /* We go by the POSIX book on this one. |
||
1068 | * |
||
1069 | * If we call a destructor then there is a chance that some new |
||
1070 | * TLS variables got set by code called in that destructor. |
||
1071 | * |
||
1072 | * Loop until nothing is left. |
||
1073 | */ |
||
1074 | dtors_called = FALSE; |
||
1075 | |||
1076 | for (dtor = g_private_destructors; dtor; dtor = dtor->next) |
||
1077 | { |
||
1078 | gpointer value; |
||
1079 | |||
1080 | value = TlsGetValue (dtor->index); |
||
1081 | if (value != NULL && dtor->notify != NULL) |
||
1082 | { |
||
1083 | /* POSIX says to clear this before the call */ |
||
1084 | TlsSetValue (dtor->index, NULL); |
||
1085 | dtor->notify (value); |
||
1086 | dtors_called = TRUE; |
||
1087 | } |
||
1088 | } |
||
1089 | } |
||
1090 | while (dtors_called); |
||
1091 | |||
1092 | if (g_thread_impl_vtable.CallThisOnThreadExit) |
||
1093 | g_thread_impl_vtable.CallThisOnThreadExit (); |
||
1094 | } |
||
1095 | |||
1096 | void |
||
1097 | g_thread_win32_process_detach (void) |
||
1098 | { |
||
1099 | #ifndef _MSC_VER |
||
1100 | if (SetThreadName_VEH_handle != NULL) |
||
1101 | { |
||
1102 | RemoveVectoredExceptionHandler (SetThreadName_VEH_handle); |
||
1103 | SetThreadName_VEH_handle = NULL; |
||
1104 | } |
||
1105 | #endif |
||
1106 | } |
||
1107 | |||
1108 | /* vim:set foldmethod=marker: */ |