Branch data Line data Source code
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 : : * SPDX-License-Identifier: LGPL-2.1-or-later
9 : : *
10 : : * This library is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU Lesser General Public
12 : : * License as published by the Free Software Foundation; either
13 : : * version 2.1 of the License, or (at your option) any later version.
14 : : *
15 : : * This library is distributed in the hope that it will be useful,
16 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 : : * Lesser General Public License for more details.
19 : : *
20 : : * You should have received a copy of the GNU Lesser General Public
21 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 : : */
23 : :
24 : : /*
25 : : * Modified by the GLib Team and others 1997-2000. See the AUTHORS
26 : : * file for a list of people on the GLib Team. See the ChangeLog
27 : : * files for a list of changes. These files are distributed with
28 : : * GLib at ftp://ftp.gtk.org/pub/gtk/.
29 : : */
30 : :
31 : : /* The GMutex and GCond implementations in this file are some of the
32 : : * lowest-level code in GLib. All other parts of GLib (messages,
33 : : * memory, slices, etc) assume that they can freely use these facilities
34 : : * without risking recursion.
35 : : *
36 : : * As such, these functions are NOT permitted to call any other part of
37 : : * GLib.
38 : : *
39 : : * The thread manipulation functions (create, exit, join, etc.) have
40 : : * more freedom -- they can do as they please.
41 : : */
42 : :
43 : : #include "config.h"
44 : :
45 : : #include "glib.h"
46 : : #include "glib-init.h"
47 : : #include "gthread.h"
48 : : #include "gthreadprivate.h"
49 : : #include "gslice.h"
50 : :
51 : : #include <windows.h>
52 : :
53 : : #include <process.h>
54 : : #include <stdlib.h>
55 : : #include <stdio.h>
56 : :
57 : : static void
58 : 0 : g_thread_abort (gint status,
59 : : const gchar *function)
60 : : {
61 : 0 : fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s. Aborting.\n",
62 : 0 : strerror (status), function);
63 : 0 : g_abort ();
64 : : }
65 : :
66 : : /* Starting with Vista and Windows 2008, we have access to the
67 : : * CONDITION_VARIABLE and SRWLock primitives on Windows, which are
68 : : * pretty reasonable approximations of the primitives specified in
69 : : * POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively).
70 : : *
71 : : * Both of these types are structs containing a single pointer. That
72 : : * pointer is used as an atomic bitfield to support user-space mutexes
73 : : * that only get the kernel involved in cases of contention (similar
74 : : * to how futex()-based mutexes work on Linux). The biggest advantage
75 : : * of these new types is that they can be statically initialised to
76 : : * zero. That means that they are completely ABI compatible with our
77 : : * GMutex and GCond APIs.
78 : : */
79 : :
80 : : /* {{{1 GMutex */
81 : : G_ALWAYS_INLINE static inline void
82 : 127821 : g_mutex_init_impl (GMutex *mutex)
83 : : {
84 : 127821 : InitializeSRWLock ((gpointer) mutex);
85 : 127821 : }
86 : :
87 : : G_ALWAYS_INLINE static inline void
88 : 126938 : g_mutex_clear_impl (GMutex *mutex)
89 : : {
90 : 126938 : }
91 : :
92 : : G_ALWAYS_INLINE static inline void
93 : 264676887 : g_mutex_lock_impl (GMutex *mutex)
94 : : {
95 : 264676887 : AcquireSRWLockExclusive ((gpointer) mutex);
96 : 264676887 : }
97 : :
98 : : G_ALWAYS_INLINE static inline gboolean
99 : 1091417 : g_mutex_trylock_impl (GMutex *mutex)
100 : : {
101 : 1091417 : return TryAcquireSRWLockExclusive ((gpointer) mutex);
102 : : }
103 : :
104 : : G_ALWAYS_INLINE static inline void
105 : 267368347 : g_mutex_unlock_impl (GMutex *mutex)
106 : : {
107 : 267368347 : ReleaseSRWLockExclusive ((gpointer) mutex);
108 : 267368347 : }
109 : :
110 : : /* {{{1 GRecMutex */
111 : :
112 : : static CRITICAL_SECTION *
113 : 393 : g_rec_mutex_impl_new (void)
114 : : {
115 : : CRITICAL_SECTION *cs;
116 : :
117 : 393 : cs = g_slice_new (CRITICAL_SECTION);
118 : 393 : InitializeCriticalSection (cs);
119 : :
120 : 393 : return cs;
121 : : }
122 : :
123 : : static void
124 : 20 : g_rec_mutex_impl_free (CRITICAL_SECTION *cs)
125 : : {
126 : 20 : DeleteCriticalSection (cs);
127 : 20 : g_slice_free (CRITICAL_SECTION, cs);
128 : 20 : }
129 : :
130 : : static CRITICAL_SECTION *
131 : 992681 : g_rec_mutex_get_impl (GRecMutex *mutex)
132 : : {
133 : 992681 : CRITICAL_SECTION *impl = mutex->p;
134 : :
135 : 992681 : if G_UNLIKELY (mutex->p == NULL)
136 : : {
137 : 373 : impl = g_rec_mutex_impl_new ();
138 : 373 : if (InterlockedCompareExchangePointer (&mutex->p, impl, NULL) != NULL)
139 : 0 : g_rec_mutex_impl_free (impl);
140 : 373 : impl = mutex->p;
141 : 373 : }
142 : :
143 : 992681 : return impl;
144 : : }
145 : :
146 : : G_ALWAYS_INLINE static inline void
147 : 20 : g_rec_mutex_init_impl (GRecMutex *mutex)
148 : : {
149 : 20 : mutex->p = g_rec_mutex_impl_new ();
150 : 20 : }
151 : :
152 : : G_ALWAYS_INLINE static inline void
153 : 20 : g_rec_mutex_clear_impl (GRecMutex *mutex)
154 : : {
155 : 20 : g_rec_mutex_impl_free (mutex->p);
156 : 20 : }
157 : :
158 : : G_ALWAYS_INLINE static inline void
159 : 982700 : g_rec_mutex_lock_impl (GRecMutex *mutex)
160 : : {
161 : 982700 : EnterCriticalSection (g_rec_mutex_get_impl (mutex));
162 : 982700 : }
163 : :
164 : : G_ALWAYS_INLINE static inline void
165 : 985123 : g_rec_mutex_unlock_impl (GRecMutex *mutex)
166 : : {
167 : 985123 : LeaveCriticalSection (mutex->p);
168 : 985123 : }
169 : :
170 : : G_ALWAYS_INLINE static inline gboolean
171 : 9999 : g_rec_mutex_trylock_impl (GRecMutex *mutex)
172 : : {
173 : 9999 : return TryEnterCriticalSection (g_rec_mutex_get_impl (mutex));
174 : : }
175 : :
176 : : /* {{{1 GRWLock */
177 : :
178 : : G_ALWAYS_INLINE static inline void
179 : 51 : g_rw_lock_init_impl (GRWLock *lock)
180 : : {
181 : 51 : InitializeSRWLock ((gpointer) lock);
182 : 51 : }
183 : :
184 : : G_ALWAYS_INLINE static inline void
185 : 51 : g_rw_lock_clear_impl (GRWLock *lock)
186 : : {
187 : 51 : }
188 : :
189 : : G_ALWAYS_INLINE static inline void
190 : 1194353 : g_rw_lock_writer_lock_impl (GRWLock *lock)
191 : : {
192 : 1194353 : AcquireSRWLockExclusive ((gpointer) lock);
193 : 1194353 : }
194 : :
195 : : G_ALWAYS_INLINE static inline gboolean
196 : 991493 : g_rw_lock_writer_trylock_impl (GRWLock *lock)
197 : : {
198 : 991493 : return TryAcquireSRWLockExclusive ((gpointer) lock);
199 : : }
200 : :
201 : : G_ALWAYS_INLINE static inline void
202 : 1437020 : g_rw_lock_writer_unlock_impl (GRWLock *lock)
203 : : {
204 : 1437020 : ReleaseSRWLockExclusive ((gpointer) lock);
205 : 1437020 : }
206 : :
207 : : G_ALWAYS_INLINE static inline void
208 : 15097978 : g_rw_lock_reader_lock_impl (GRWLock *lock)
209 : : {
210 : 15097978 : AcquireSRWLockShared ((gpointer) lock);
211 : 15097978 : }
212 : :
213 : : G_ALWAYS_INLINE static inline gboolean
214 : 6 : g_rw_lock_reader_trylock_impl (GRWLock *lock)
215 : : {
216 : 6 : return TryAcquireSRWLockShared ((gpointer) lock);
217 : : }
218 : :
219 : : G_ALWAYS_INLINE static inline void
220 : 15117824 : g_rw_lock_reader_unlock_impl (GRWLock *lock)
221 : : {
222 : 15117824 : ReleaseSRWLockShared ((gpointer) lock);
223 : 15117824 : }
224 : :
225 : : /* {{{1 GCond */
226 : : G_ALWAYS_INLINE static inline void
227 : 2605099 : g_cond_init_impl (GCond *cond)
228 : : {
229 : 2605099 : InitializeConditionVariable ((gpointer) cond);
230 : 2605099 : }
231 : :
232 : : G_ALWAYS_INLINE static inline void
233 : 2604022 : g_cond_clear_impl (GCond *cond)
234 : : {
235 : 2604022 : }
236 : :
237 : : G_ALWAYS_INLINE static inline void
238 : 10861454 : g_cond_signal_impl (GCond *cond)
239 : : {
240 : 10861454 : WakeConditionVariable ((gpointer) cond);
241 : 10861454 : }
242 : :
243 : : G_ALWAYS_INLINE static inline void
244 : 151741 : g_cond_broadcast_impl (GCond *cond)
245 : : {
246 : 151741 : WakeAllConditionVariable ((gpointer) cond);
247 : 151741 : }
248 : :
249 : : G_ALWAYS_INLINE static inline void
250 : 5039678 : g_cond_wait_impl (GCond *cond,
251 : : GMutex *entered_mutex)
252 : : {
253 : 5039678 : SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, INFINITE, 0);
254 : 5039678 : }
255 : :
256 : : G_ALWAYS_INLINE static inline gboolean
257 : 708 : g_cond_wait_until_impl (GCond *cond,
258 : : GMutex *entered_mutex,
259 : : gint64 end_time)
260 : : {
261 : : gint64 span, start_time;
262 : : DWORD span_millis;
263 : : gboolean signalled;
264 : :
265 : 708 : start_time = g_get_monotonic_time ();
266 : 708 : do
267 : : {
268 : 739 : span = end_time - start_time;
269 : :
270 : 739 : if G_UNLIKELY (span < 0)
271 : 0 : span_millis = 0;
272 : 739 : else if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * (DWORD) INFINITE)
273 : 0 : span_millis = INFINITE;
274 : : else
275 : : /* Round up so we don't time out too early */
276 : 739 : span_millis = (span + 1000 - 1) / 1000;
277 : :
278 : : /* We never want to wait infinitely */
279 : 739 : if (span_millis >= INFINITE)
280 : 0 : span_millis = INFINITE - 1;
281 : :
282 : 739 : signalled = SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, span_millis, 0);
283 : 739 : if (signalled)
284 : 445 : break;
285 : :
286 : : /* In case we didn't wait long enough after a timeout, wait again for the
287 : : * remaining time */
288 : 294 : start_time = g_get_monotonic_time ();
289 : 294 : }
290 : 294 : while (start_time < end_time);
291 : :
292 : 708 : return signalled;
293 : : }
294 : :
295 : : /* {{{1 GPrivate */
296 : :
297 : : typedef struct _GPrivateDestructor GPrivateDestructor;
298 : :
299 : : struct _GPrivateDestructor
300 : : {
301 : : DWORD index;
302 : : GDestroyNotify notify;
303 : : GPrivateDestructor *next;
304 : : };
305 : :
306 : : static GPrivateDestructor *g_private_destructors; /* (atomic) prepend-only */
307 : : static CRITICAL_SECTION g_private_lock;
308 : :
309 : : static DWORD
310 : 17241999 : _g_private_get_impl (GPrivate *key)
311 : : {
312 : 17241999 : DWORD impl = (DWORD) GPOINTER_TO_UINT(key->p);
313 : :
314 : 17241999 : if G_UNLIKELY (impl == 0)
315 : : {
316 : 1018 : EnterCriticalSection (&g_private_lock);
317 : 1018 : impl = (UINT_PTR) key->p;
318 : 1018 : if (impl == 0)
319 : : {
320 : : GPrivateDestructor *destructor;
321 : :
322 : 1015 : impl = TlsAlloc ();
323 : :
324 : 1015 : if G_UNLIKELY (impl == 0)
325 : : {
326 : : /* Ignore TLS index 0 temporarily (as 0 is the indicator that we
327 : : * haven't allocated TLS yet) and alloc again;
328 : : * See https://gitlab.gnome.org/GNOME/glib/-/issues/2058 */
329 : 0 : DWORD impl2 = TlsAlloc ();
330 : 0 : TlsFree (impl);
331 : 0 : impl = impl2;
332 : 0 : }
333 : :
334 : 1015 : if (impl == TLS_OUT_OF_INDEXES || impl == 0)
335 : 0 : g_thread_abort (0, "TlsAlloc");
336 : :
337 : 1015 : if (key->notify != NULL)
338 : : {
339 : 590 : destructor = malloc (sizeof (GPrivateDestructor));
340 : 590 : if G_UNLIKELY (destructor == NULL)
341 : 0 : g_thread_abort (errno, "malloc");
342 : 590 : destructor->index = impl;
343 : 590 : destructor->notify = key->notify;
344 : 590 : destructor->next = g_atomic_pointer_get (&g_private_destructors);
345 : :
346 : : /* We need to do an atomic store due to the unlocked
347 : : * access to the destructor list from the thread exit
348 : : * function.
349 : : *
350 : : * It can double as a sanity check...
351 : : */
352 : 1180 : if (!g_atomic_pointer_compare_and_exchange (&g_private_destructors,
353 : 590 : destructor->next,
354 : 590 : destructor))
355 : 0 : g_thread_abort (0, "g_private_get_impl(1)");
356 : 590 : }
357 : :
358 : : /* Ditto, due to the unlocked access on the fast path */
359 : 1015 : if (!g_atomic_pointer_compare_and_exchange (&key->p, NULL, GUINT_TO_POINTER (impl)))
360 : 0 : g_thread_abort (0, "g_private_get_impl(2)");
361 : 1015 : }
362 : 1018 : LeaveCriticalSection (&g_private_lock);
363 : 1018 : }
364 : :
365 : 17241999 : return impl;
366 : : }
367 : :
368 : : G_ALWAYS_INLINE static inline gpointer
369 : 10100281 : g_private_get_impl (GPrivate *key)
370 : : {
371 : 10100281 : return TlsGetValue (_g_private_get_impl (key));
372 : : }
373 : :
374 : : G_ALWAYS_INLINE static inline void
375 : 7141553 : g_private_set_impl (GPrivate *key,
376 : : gpointer value)
377 : : {
378 : 7141553 : TlsSetValue (_g_private_get_impl (key), value);
379 : 7141553 : }
380 : :
381 : : G_ALWAYS_INLINE static inline void
382 : 680 : g_private_replace_impl (GPrivate *key,
383 : : gpointer value)
384 : : {
385 : 680 : DWORD impl = _g_private_get_impl (key);
386 : : gpointer old;
387 : :
388 : 680 : old = TlsGetValue (impl);
389 : 680 : TlsSetValue (impl, value);
390 : 680 : if (old && key->notify)
391 : 71 : key->notify (old);
392 : 680 : }
393 : :
394 : : /* {{{1 GThread */
395 : :
396 : : #define win32_check_for_error(what) G_STMT_START{ \
397 : : if (!(what)) \
398 : : g_error ("file %s: line %d (%s): error %s during %s", \
399 : : __FILE__, __LINE__, G_STRFUNC, \
400 : : g_win32_error_message (GetLastError ()), #what); \
401 : : }G_STMT_END
402 : :
403 : : #define G_MUTEX_SIZE (sizeof (gpointer))
404 : :
405 : : typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
406 : :
407 : : typedef struct
408 : : {
409 : : GRealThread thread;
410 : :
411 : : GThreadFunc proxy;
412 : : HANDLE handle;
413 : : } GThreadWin32;
414 : :
415 : : void
416 : 28945 : g_system_thread_free (GRealThread *thread)
417 : : {
418 : 28945 : GThreadWin32 *wt = (GThreadWin32 *) thread;
419 : :
420 : 28945 : win32_check_for_error (CloseHandle (wt->handle));
421 : 28945 : g_slice_free (GThreadWin32, wt);
422 : 28945 : }
423 : :
424 : : void
425 : 28940 : g_system_thread_exit (void)
426 : : {
427 : 28940 : _endthreadex (0);
428 : : }
429 : :
430 : : static guint __stdcall
431 : 29190 : g_thread_win32_proxy (gpointer data)
432 : : {
433 : 29190 : GThreadWin32 *self = data;
434 : :
435 : 29190 : self->proxy (self);
436 : :
437 : 29190 : g_system_thread_exit ();
438 : :
439 : : g_assert_not_reached ();
440 : :
441 : : return 0;
442 : : }
443 : :
444 : : GRealThread *
445 : 29276 : g_system_thread_new (GThreadFunc proxy,
446 : : gulong stack_size,
447 : : const char *name,
448 : : GThreadFunc func,
449 : : gpointer data,
450 : : GError **error)
451 : : {
452 : : GThreadWin32 *thread;
453 : : GRealThread *base_thread;
454 : : guint ignore;
455 : 29276 : const gchar *message = NULL;
456 : : int thread_prio;
457 : :
458 : 29276 : thread = g_slice_new0 (GThreadWin32);
459 : 29276 : thread->proxy = proxy;
460 : 29276 : thread->handle = (HANDLE) NULL;
461 : 29276 : base_thread = (GRealThread*)thread;
462 : 29276 : base_thread->ref_count = 2;
463 : 29276 : base_thread->ours = TRUE;
464 : 29276 : base_thread->thread.joinable = TRUE;
465 : 29276 : base_thread->thread.func = func;
466 : 29276 : base_thread->thread.data = data;
467 : 29276 : if (name)
468 : 2392 : g_strlcpy (base_thread->name, name, 16);
469 : :
470 : 29276 : thread->handle = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_win32_proxy, thread,
471 : : CREATE_SUSPENDED, &ignore);
472 : :
473 : 29276 : if (thread->handle == NULL)
474 : : {
475 : 0 : message = "Error creating thread";
476 : 0 : goto error;
477 : : }
478 : :
479 : : /* For thread priority inheritance we need to manually set the thread
480 : : * priority of the new thread to the priority of the current thread. We
481 : : * also have to start the thread suspended and resume it after actually
482 : : * setting the priority here.
483 : : *
484 : : * On Windows, by default all new threads are created with NORMAL thread
485 : : * priority.
486 : : */
487 : : {
488 : 29276 : HANDLE current_thread = GetCurrentThread ();
489 : 29276 : thread_prio = GetThreadPriority (current_thread);
490 : : }
491 : :
492 : 29276 : if (thread_prio != THREAD_PRIORITY_ERROR_RETURN)
493 : : {
494 : : /* GetThreadPriority() may return non-predefined priority level
495 : : * which is not acceptable by SetThreadPriority().
496 : : * Ignores any errors occurred here since it's not a critical
497 : : */
498 : 29275 : if (!SetThreadPriority (thread->handle, thread_prio))
499 : : {
500 : 0 : gchar *win_error = g_win32_error_message (GetLastError ());
501 : 0 : g_debug ("SetThreadPriority with priority %d failed: %s",
502 : 0 : thread_prio, win_error);
503 : 0 : g_free (win_error);
504 : 0 : }
505 : 29275 : }
506 : :
507 : 29276 : if (ResumeThread (thread->handle) == (DWORD) -1)
508 : : {
509 : 0 : message = "Error resuming new thread";
510 : 0 : goto error;
511 : : }
512 : :
513 : 29274 : return (GRealThread *) thread;
514 : :
515 : : error:
516 : : {
517 : 0 : gchar *win_error = g_win32_error_message (GetLastError ());
518 : 0 : g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
519 : 0 : "%s: %s", message, win_error);
520 : 0 : g_free (win_error);
521 : 0 : if (thread->handle)
522 : 0 : CloseHandle (thread->handle);
523 : 0 : g_slice_free (GThreadWin32, thread);
524 : 0 : return NULL;
525 : : }
526 : 29274 : }
527 : :
528 : : G_ALWAYS_INLINE static inline void
529 : 19584091 : g_thread_yield_impl (void)
530 : : {
531 : 19584091 : Sleep(0);
532 : 19584091 : }
533 : :
534 : : void
535 : 28698 : g_system_thread_wait (GRealThread *thread)
536 : : {
537 : 28698 : GThreadWin32 *wt = (GThreadWin32 *) thread;
538 : :
539 : 28698 : win32_check_for_error (WAIT_FAILED != WaitForSingleObject (wt->handle, INFINITE));
540 : 28698 : }
541 : :
542 : : #define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388)
543 : :
544 : : #ifndef _MSC_VER
545 : : static void *SetThreadName_VEH_handle = NULL;
546 : :
547 : : static LONG __stdcall
548 : 0 : SetThreadName_VEH (PEXCEPTION_POINTERS ExceptionInfo)
549 : : {
550 : 0 : if (ExceptionInfo->ExceptionRecord != NULL &&
551 : 0 : ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME)
552 : 0 : return EXCEPTION_CONTINUE_EXECUTION;
553 : :
554 : 0 : return EXCEPTION_CONTINUE_SEARCH;
555 : 0 : }
556 : : #endif
557 : :
558 : : typedef struct _THREADNAME_INFO
559 : : {
560 : : DWORD dwType; /* must be 0x1000 */
561 : : LPCSTR szName; /* pointer to name (in user addr space) */
562 : : DWORD dwThreadID; /* thread ID (-1=caller thread) */
563 : : DWORD dwFlags; /* reserved for future use, must be zero */
564 : : } THREADNAME_INFO;
565 : :
566 : : static void
567 : 0 : SetThreadName (DWORD dwThreadID,
568 : : LPCSTR szThreadName)
569 : : {
570 : : THREADNAME_INFO info;
571 : : DWORD infosize;
572 : :
573 : 0 : info.dwType = 0x1000;
574 : 0 : info.szName = szThreadName;
575 : 0 : info.dwThreadID = dwThreadID;
576 : 0 : info.dwFlags = 0;
577 : :
578 : 0 : infosize = sizeof (info) / sizeof (ULONG_PTR);
579 : :
580 : : #ifdef _MSC_VER
581 : : __try
582 : : {
583 : : RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize,
584 : : (const ULONG_PTR *) &info);
585 : : }
586 : : __except (GetExceptionCode () == EXCEPTION_SET_THREAD_NAME ?
587 : : EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
588 : : {
589 : : }
590 : : #else
591 : 0 : if ((!IsDebuggerPresent ()) || (SetThreadName_VEH_handle == NULL))
592 : 0 : return;
593 : :
594 : 0 : RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (const ULONG_PTR *) &info);
595 : : #endif
596 : 0 : }
597 : :
598 : : typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread,
599 : : PCWSTR lpThreadDescription);
600 : : static pSetThreadDescription SetThreadDescriptionFunc = NULL;
601 : : static HMODULE kernel32_module = NULL;
602 : :
603 : : static gboolean
604 : 2306 : g_thread_win32_load_library (void)
605 : : {
606 : : /* FIXME: Add support for UWP app */
607 : : #if !defined(G_WINAPI_ONLY_APP)
608 : : static gsize _init_once = 0;
609 : 2306 : if (g_once_init_enter (&_init_once))
610 : : {
611 : 161 : kernel32_module = LoadLibraryW (L"kernel32.dll");
612 : 161 : if (kernel32_module)
613 : : {
614 : 161 : SetThreadDescriptionFunc =
615 : 161 : (pSetThreadDescription) GetProcAddress (kernel32_module,
616 : : "SetThreadDescription");
617 : 161 : if (!SetThreadDescriptionFunc)
618 : 0 : FreeLibrary (kernel32_module);
619 : 161 : }
620 : 161 : g_once_init_leave (&_init_once, 1);
621 : 161 : }
622 : : #endif
623 : :
624 : 2306 : return !!SetThreadDescriptionFunc;
625 : : }
626 : :
627 : : static gboolean
628 : 2308 : g_thread_win32_set_thread_desc (const gchar *name)
629 : : {
630 : : HRESULT hr;
631 : : wchar_t *namew;
632 : :
633 : 2308 : if (!g_thread_win32_load_library () || !name)
634 : 2 : return FALSE;
635 : :
636 : 2306 : namew = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL);
637 : 2306 : if (!namew)
638 : 0 : return FALSE;
639 : :
640 : 2306 : hr = SetThreadDescriptionFunc (GetCurrentThread (), namew);
641 : :
642 : 2306 : g_free (namew);
643 : 2306 : return SUCCEEDED (hr);
644 : 2306 : }
645 : :
646 : : void
647 : 2306 : g_system_thread_set_name (const gchar *name)
648 : : {
649 : : /* Prefer SetThreadDescription over exception based way if available,
650 : : * since thread description set by SetThreadDescription will be preserved
651 : : * in dump file */
652 : 2306 : if (!g_thread_win32_set_thread_desc (name))
653 : 0 : SetThreadName ((DWORD) -1, name);
654 : 2306 : }
655 : :
656 : : void
657 : 26883 : g_system_thread_get_name (char *buffer,
658 : : gsize length)
659 : : {
660 : : /* FIXME: Not implemented yet */
661 : 26883 : g_assert (length >= 1);
662 : 26883 : buffer[0] = '\0';
663 : 26883 : }
664 : :
665 : : /* {{{1 Epilogue */
666 : :
667 : : void
668 : 1217 : g_thread_win32_init (void)
669 : : {
670 : 1217 : InitializeCriticalSection (&g_private_lock);
671 : :
672 : : #ifndef _MSC_VER
673 : : /* Set the handler as last to not interfere with ASAN runtimes.
674 : : * Many ASAN implementations (currently all three of GCC, CLANG
675 : : * and MSVC) install a Vectored Exception Handler that must be
676 : : * first in the sequence to work well
677 : : */
678 : 1217 : SetThreadName_VEH_handle = AddVectoredExceptionHandler (0, &SetThreadName_VEH);
679 : 1217 : if (SetThreadName_VEH_handle == NULL)
680 : 0 : g_critical ("%s failed with error code %u",
681 : 0 : "AddVectoredExceptionHandler", (unsigned int) GetLastError ());
682 : : #endif
683 : 1217 : }
684 : :
685 : : void
686 : 29539 : g_thread_win32_thread_detach (void)
687 : : {
688 : : gboolean dtors_called;
689 : :
690 : 29539 : do
691 : : {
692 : : GPrivateDestructor *dtor;
693 : :
694 : : /* We go by the POSIX book on this one.
695 : : *
696 : : * If we call a destructor then there is a chance that some new
697 : : * TLS variables got set by code called in that destructor.
698 : : *
699 : : * Loop until nothing is left.
700 : : */
701 : 58485 : dtors_called = FALSE;
702 : :
703 : 224443 : for (dtor = g_atomic_pointer_get (&g_private_destructors); dtor; dtor = dtor->next)
704 : : {
705 : : gpointer value;
706 : :
707 : 165958 : value = TlsGetValue (dtor->index);
708 : 165958 : if (value != NULL && dtor->notify != NULL)
709 : : {
710 : : /* POSIX says to clear this before the call */
711 : 33851 : TlsSetValue (dtor->index, NULL);
712 : 33851 : dtor->notify (value);
713 : 33851 : dtors_called = TRUE;
714 : 33851 : }
715 : 165958 : }
716 : 58485 : }
717 : 58485 : while (dtors_called);
718 : 29539 : }
719 : :
720 : : void
721 : 1214 : g_thread_win32_process_detach (void)
722 : : {
723 : : #ifndef _MSC_VER
724 : 1214 : if (SetThreadName_VEH_handle != NULL)
725 : : {
726 : 1214 : RemoveVectoredExceptionHandler (SetThreadName_VEH_handle);
727 : 1214 : SetThreadName_VEH_handle = NULL;
728 : 1214 : }
729 : : #endif
730 : 1214 : }
731 : :
732 : : /* vim:set foldmethod=marker: */
|