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, write to the 20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 21 * Boston, MA 02111-1307, USA. 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 /* 32 * MT safe 33 */ 34 35 #include "config.h" 36 37 #include "glib.h" 38 39 #define STRICT 40 #define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */ 41 #include <windows.h> 42 #undef STRICT 43 44 #include <process.h> 45 #include <stdlib.h> 46 #include <stdio.h> 47 48 #define win32_check_for_error(what) G_STMT_START{ \ 49 if (!(what)) \ 50 g_error ("file %s: line %d (%s): error %s during %s", \ 51 __FILE__, __LINE__, G_STRFUNC, \ 52 g_win32_error_message (GetLastError ()), #what); \ 53 }G_STMT_END 54 55 #define G_MUTEX_SIZE (sizeof (gpointer)) 56 57 #define PRIORITY_LOW_VALUE THREAD_PRIORITY_BELOW_NORMAL 58 #define PRIORITY_NORMAL_VALUE THREAD_PRIORITY_NORMAL 59 #define PRIORITY_HIGH_VALUE THREAD_PRIORITY_ABOVE_NORMAL 60 #define PRIORITY_URGENT_VALUE THREAD_PRIORITY_HIGHEST 61 62 static DWORD g_thread_self_tls; 63 static DWORD g_private_tls; 64 static DWORD g_cond_event_tls; 65 static CRITICAL_SECTION g_thread_global_spinlock; 66 67 typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *); 68 69 static GTryEnterCriticalSectionFunc try_enter_critical_section = NULL; 70 71 /* As noted in the docs, GPrivate is a limited resource, here we take 72 * a rather low maximum to save memory, use GStaticPrivate instead. */ 73 #define G_PRIVATE_MAX 100 74 75 static GDestroyNotify g_private_destructors[G_PRIVATE_MAX]; 76 77 static guint g_private_next = 0; 78 79 /* A "forward" declaration of this structure */ 80 static GThreadFunctions g_thread_functions_for_glib_use_default; 81 82 typedef struct _GThreadData GThreadData; 83 struct _GThreadData 84 { 85 GThreadFunc func; 86 gpointer data; 87 HANDLE thread; 88 gboolean joinable; 89 }; 90 91 struct _GCond 92 { 93 GPtrArray *array; 94 CRITICAL_SECTION lock; 95 }; 96 97 static GMutex * 98 g_mutex_new_win32_cs_impl (void) 99 { 100 CRITICAL_SECTION *cs = g_new (CRITICAL_SECTION, 1); 101 gpointer *retval = g_new (gpointer, 1); 102 103 InitializeCriticalSection (cs); 104 *retval = cs; 105 return (GMutex *) retval; 106 } 107 108 static void 109 g_mutex_free_win32_cs_impl (GMutex *mutex) 110 { 111 gpointer *ptr = (gpointer *) mutex; 112 CRITICAL_SECTION *cs = (CRITICAL_SECTION *) *ptr; 113 114 DeleteCriticalSection (cs); 115 g_free (cs); 116 g_free (mutex); 117 } 118 119 /* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use 120 functions from gmem.c and gmessages.c; */ 121 122 static void 123 g_mutex_lock_win32_cs_impl (GMutex *mutex) 124 { 125 EnterCriticalSection (*(CRITICAL_SECTION **)mutex); 126 } 127 128 static gboolean 129 g_mutex_trylock_win32_cs_impl (GMutex * mutex) 130 { 131 return try_enter_critical_section (*(CRITICAL_SECTION **)mutex); 132 } 133 134 static void 135 g_mutex_unlock_win32_cs_impl (GMutex *mutex) 136 { 137 LeaveCriticalSection (*(CRITICAL_SECTION **)mutex); 138 } 139 140 static GMutex * 141 g_mutex_new_win32_impl (void) 142 { 143 HANDLE handle; 144 HANDLE *retval; 145 win32_check_for_error (handle = CreateMutex (NULL, FALSE, NULL)); 146 retval = g_new (HANDLE, 1); 147 *retval = handle; 148 return (GMutex *) retval; 149 } 150 151 static void 152 g_mutex_free_win32_impl (GMutex *mutex) 153 { 154 win32_check_for_error (CloseHandle (*(HANDLE *) mutex)); 155 g_free (mutex); 156 } 157 158 /* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use 159 functions from gmem.c and gmessages.c; */ 160 161 static void 162 g_mutex_lock_win32_impl (GMutex *mutex) 163 { 164 WaitForSingleObject (*(HANDLE *) mutex, INFINITE); 165 } 166 167 static gboolean 168 g_mutex_trylock_win32_impl (GMutex * mutex) 169 { 170 DWORD result; 171 win32_check_for_error (WAIT_FAILED != 172 (result = WaitForSingleObject (*(HANDLE *)mutex, 0))); 173 return result != WAIT_TIMEOUT; 174 } 175 176 static void 177 g_mutex_unlock_win32_impl (GMutex *mutex) 178 { 179 ReleaseMutex (*(HANDLE *) mutex); 180 } 181 182 static GCond * 183 g_cond_new_win32_impl (void) 184 { 185 GCond *retval = g_new (GCond, 1); 186 187 retval->array = g_ptr_array_new (); 188 InitializeCriticalSection (&retval->lock); 189 190 return retval; 191 } 192 193 static void 194 g_cond_signal_win32_impl (GCond * cond) 195 { 196 EnterCriticalSection (&cond->lock); 197 198 if (cond->array->len > 0) 199 { 200 SetEvent (g_ptr_array_index (cond->array, 0)); 201 g_ptr_array_remove_index (cond->array, 0); 202 } 203 204 LeaveCriticalSection (&cond->lock); 205 } 206 207 static void 208 g_cond_broadcast_win32_impl (GCond * cond) 209 { 210 guint i; 211 EnterCriticalSection (&cond->lock); 212 213 for (i = 0; i < cond->array->len; i++) 214 SetEvent (g_ptr_array_index (cond->array, i)); 215 216 g_ptr_array_set_size (cond->array, 0); 217 LeaveCriticalSection (&cond->lock); 218 } 219 220 static gboolean 221 g_cond_wait_internal (GCond *cond, 222 GMutex *entered_mutex, 223 gulong milliseconds) 224 { 225 gulong retval; 226 HANDLE event = TlsGetValue (g_cond_event_tls); 227 228 if (!event) 229 { 230 win32_check_for_error (event = CreateEvent (0, FALSE, FALSE, NULL)); 231 TlsSetValue (g_cond_event_tls, event); 232 } 233 234 EnterCriticalSection (&cond->lock); 235 236 /* The event must not be signaled. Check this */ 237 g_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT); 238 239 g_ptr_array_add (cond->array, event); 240 LeaveCriticalSection (&cond->lock); 241 242 g_thread_functions_for_glib_use_default.mutex_unlock (entered_mutex); 243 244 win32_check_for_error (WAIT_FAILED != 245 (retval = WaitForSingleObject (event, milliseconds))); 246 247 g_thread_functions_for_glib_use_default.mutex_lock (entered_mutex); 248 249 if (retval == WAIT_TIMEOUT) 250 { 251 EnterCriticalSection (&cond->lock); 252 g_ptr_array_remove (cond->array, event); 253 254 /* In the meantime we could have been signaled, so we must again 255 * wait for the signal, this time with no timeout, to reset 256 * it. retval is set again to honour the late arrival of the 257 * signal */ 258 win32_check_for_error (WAIT_FAILED != 259 (retval = WaitForSingleObject (event, 0))); 260 261 LeaveCriticalSection (&cond->lock); 262 } 263 264 #ifndef G_DISABLE_ASSERT 265 EnterCriticalSection (&cond->lock); 266 267 /* Now event must not be inside the array, check this */ 268 g_assert (g_ptr_array_remove (cond->array, event) == FALSE); 269 270 LeaveCriticalSection (&cond->lock); 271 #endif /* !G_DISABLE_ASSERT */ 272 273 return retval != WAIT_TIMEOUT; 274 } 275 276 static void 277 g_cond_wait_win32_impl (GCond *cond, 278 GMutex *entered_mutex) 279 { 280 g_return_if_fail (cond != NULL); 281 g_return_if_fail (entered_mutex != NULL); 282 283 g_cond_wait_internal (cond, entered_mutex, INFINITE); 284 } 285 286 static gboolean 287 g_cond_timed_wait_win32_impl (GCond *cond, 288 GMutex *entered_mutex, 289 GTimeVal *abs_time) 290 { 291 GTimeVal current_time; 292 gulong to_wait; 293 294 g_return_val_if_fail (cond != NULL, FALSE); 295 g_return_val_if_fail (entered_mutex != NULL, FALSE); 296 297 if (!abs_time) 298 to_wait = INFINITE; 299 else 300 { 301 g_get_current_time (¤t_time); 302 if (abs_time->tv_sec < current_time.tv_sec || 303 (abs_time->tv_sec == current_time.tv_sec && 304 abs_time->tv_usec <= current_time.tv_usec)) 305 to_wait = 0; 306 else 307 to_wait = (abs_time->tv_sec - current_time.tv_sec) * 1000 + 308 (abs_time->tv_usec - current_time.tv_usec) / 1000; 309 } 310 311 return g_cond_wait_internal (cond, entered_mutex, to_wait); 312 } 313 314 static void 315 g_cond_free_win32_impl (GCond * cond) 316 { 317 DeleteCriticalSection (&cond->lock); 318 g_ptr_array_free (cond->array, TRUE); 319 g_free (cond); 320 } 321 322 static GPrivate * 323 g_private_new_win32_impl (GDestroyNotify destructor) 324 { 325 GPrivate *result; 326 EnterCriticalSection (&g_thread_global_spinlock); 327 if (g_private_next >= G_PRIVATE_MAX) 328 { 329 char buf[100]; 330 sprintf (buf, 331 "Too many GPrivate allocated. Their number is limited to %d.", 332 G_PRIVATE_MAX); 333 MessageBox (NULL, buf, NULL, MB_ICONERROR|MB_SETFOREGROUND); 334 if (IsDebuggerPresent ()) 335 G_BREAKPOINT (); 336 abort (); 337 } 338 g_private_destructors[g_private_next] = destructor; 339 result = GUINT_TO_POINTER (g_private_next); 340 g_private_next++; 341 LeaveCriticalSection (&g_thread_global_spinlock); 342 343 return result; 344 } 345 346 /* NOTE: the functions g_private_get and g_private_set may not use 347 functions from gmem.c and gmessages.c */ 348 349 static void 350 g_private_set_win32_impl (GPrivate * private_key, gpointer value) 351 { 352 gpointer* array = TlsGetValue (g_private_tls); 353 guint index = GPOINTER_TO_UINT (private_key); 354 355 if (index >= G_PRIVATE_MAX) 356 return; 357 358 if (!array) 359 { 360 array = (gpointer*) calloc (G_PRIVATE_MAX, sizeof (gpointer)); 361 TlsSetValue (g_private_tls, array); 362 } 363 364 array[index] = value; 365 } 366 367 static gpointer 368 g_private_get_win32_impl (GPrivate * private_key) 369 { 370 gpointer* array = TlsGetValue (g_private_tls); 371 guint index = GPOINTER_TO_UINT (private_key); 372 373 if (index >= G_PRIVATE_MAX || !array) 374 return NULL; 375 376 return array[index]; 377 } 378 379 static void 380 g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority) 381 { 382 GThreadData *target = *(GThreadData **)thread; 383 384 g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW); 385 g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT); 386 387 win32_check_for_error (SetThreadPriority (target->thread, 388 g_thread_priority_map [priority])); 389 } 390 391 static void 392 g_thread_self_win32_impl (gpointer thread) 393 { 394 GThreadData *self = TlsGetValue (g_thread_self_tls); 395 396 if (!self) 397 { 398 /* This should only happen for the main thread! */ 399 HANDLE handle = GetCurrentThread (); 400 HANDLE process = GetCurrentProcess (); 401 self = g_new (GThreadData, 1); 402 win32_check_for_error (DuplicateHandle (process, handle, process, 403 &self->thread, 0, FALSE, 404 DUPLICATE_SAME_ACCESS)); 405 win32_check_for_error (TlsSetValue (g_thread_self_tls, self)); 406 self->func = NULL; 407 self->data = NULL; 408 self->joinable = FALSE; 409 } 410 411 *(GThreadData **)thread = self; 412 } 413 414 static void 415 g_thread_exit_win32_impl (void) 416 { 417 GThreadData *self = TlsGetValue (g_thread_self_tls); 418 guint i, private_max; 419 gpointer *array = TlsGetValue (g_private_tls); 420 HANDLE event = TlsGetValue (g_cond_event_tls); 421 422 EnterCriticalSection (&g_thread_global_spinlock); 423 private_max = g_private_next; 424 LeaveCriticalSection (&g_thread_global_spinlock); 425 426 if (array) 427 { 428 gboolean some_data_non_null; 429 430 do { 431 some_data_non_null = FALSE; 432 for (i = 0; i < private_max; i++) 433 { 434 GDestroyNotify destructor = g_private_destructors[i]; 435 GDestroyNotify data = array[i]; 436 437 if (data) 438 some_data_non_null = TRUE; 439 440 array[i] = NULL; 441 442 if (destructor && data) 443 destructor (data); 444 } 445 } while (some_data_non_null); 446 447 free (array); 448 449 win32_check_for_error (TlsSetValue (g_private_tls, NULL)); 450 } 451 452 if (self) 453 { 454 if (!self->joinable) 455 { 456 win32_check_for_error (CloseHandle (self->thread)); 457 g_free (self); 458 } 459 win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL)); 460 } 461 462 if (event) 463 { 464 CloseHandle (event); 465 win32_check_for_error (TlsSetValue (g_cond_event_tls, NULL)); 466 } 467 468 _endthreadex (0); 469 } 470 471 static guint __stdcall 472 g_thread_proxy (gpointer data) 473 { 474 GThreadData *self = (GThreadData*) data; 475 476 win32_check_for_error (TlsSetValue (g_thread_self_tls, self)); 477 478 self->func (self->data); 479 480 g_thread_exit_win32_impl (); 481 482 g_assert_not_reached (); 483 484 return 0; 485 } 486 487 static void 488 g_thread_create_win32_impl (GThreadFunc func, 489 gpointer data, 490 gulong stack_size, 491 gboolean joinable, 492 gboolean bound, 493 GThreadPriority priority, 494 gpointer thread, 495 GError **error) 496 { 497 guint ignore; 498 GThreadData *retval; 499 500 g_return_if_fail (func); 501 g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW); 502 g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT); 503 504 retval = g_new(GThreadData, 1); 505 retval->func = func; 506 retval->data = data; 507 508 retval->joinable = joinable; 509 510 retval->thread = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_proxy, 511 retval, 0, &ignore); 512 513 if (retval->thread == NULL) 514 { 515 gchar *win_error = g_win32_error_message (GetLastError ()); 516 g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, 517 "Error creating thread: %s", win_error); 518 g_free (retval); 519 g_free (win_error); 520 return; 521 } 522 523 *(GThreadData **)thread = retval; 524 525 g_thread_set_priority_win32_impl (thread, priority); 526 } 527 528 static void 529 g_thread_yield_win32_impl (void) 530 { 531 Sleep(0); 532 } 533 534 static void 535 g_thread_join_win32_impl (gpointer thread) 536 { 537 GThreadData *target = *(GThreadData **)thread; 538 539 g_return_if_fail (target->joinable); 540 541 win32_check_for_error (WAIT_FAILED != 542 WaitForSingleObject (target->thread, INFINITE)); 543 544 win32_check_for_error (CloseHandle (target->thread)); 545 g_free (target); 546 } 547 548 static guint64 549 g_thread_gettime_impl (void) 550 { 551 guint64 v; 552 553 /* Returns 100s of nanoseconds since start of 1601 */ 554 GetSystemTimeAsFileTime ((FILETIME *)&v); 555 556 /* Offset to Unix epoch */ 557 v -= G_GINT64_CONSTANT (116444736000000000); 558 /* Convert to nanoseconds */ 559 v *= 100; 560 561 return v; 562 } 563 564 static GThreadFunctions g_thread_functions_for_glib_use_default = 565 { 566 g_mutex_new_win32_impl, /* mutex */ 567 g_mutex_lock_win32_impl, 568 g_mutex_trylock_win32_impl, 569 g_mutex_unlock_win32_impl, 570 g_mutex_free_win32_impl, 571 g_cond_new_win32_impl, /* condition */ 572 g_cond_signal_win32_impl, 573 g_cond_broadcast_win32_impl, 574 g_cond_wait_win32_impl, 575 g_cond_timed_wait_win32_impl, 576 g_cond_free_win32_impl, 577 g_private_new_win32_impl, /* private thread data */ 578 g_private_get_win32_impl, 579 g_private_set_win32_impl, 580 g_thread_create_win32_impl, /* thread */ 581 g_thread_yield_win32_impl, 582 g_thread_join_win32_impl, 583 g_thread_exit_win32_impl, 584 g_thread_set_priority_win32_impl, 585 g_thread_self_win32_impl, 586 NULL /* no equal function necessary */ 587 }; 588 589 #define HAVE_G_THREAD_IMPL_INIT 590 static void 591 g_thread_impl_init () 592 { 593 static gboolean beenhere = FALSE; 594 HMODULE kernel32; 595 596 if (beenhere) 597 return; 598 599 beenhere = TRUE; 600 601 win32_check_for_error (TLS_OUT_OF_INDEXES != 602 (g_thread_self_tls = TlsAlloc ())); 603 win32_check_for_error (TLS_OUT_OF_INDEXES != 604 (g_private_tls = TlsAlloc ())); 605 win32_check_for_error (TLS_OUT_OF_INDEXES != 606 (g_cond_event_tls = TlsAlloc ())); 607 InitializeCriticalSection (&g_thread_global_spinlock); 608 609 /* Here we are looking for TryEnterCriticalSection in KERNEL32.DLL, 610 * if it is found, we can use the in general faster critical 611 * sections instead of mutexes. See 612 * http://world.std.com/~jmhart/csmutx.htm for some discussion. 613 */ 614 kernel32 = GetModuleHandle ("KERNEL32.DLL"); 615 if (kernel32) 616 { 617 try_enter_critical_section = (GTryEnterCriticalSectionFunc) 618 GetProcAddress(kernel32, "TryEnterCriticalSection"); 619 620 /* Even if TryEnterCriticalSection is found, it is not 621 * necessarily working..., we have to check it */ 622 if (try_enter_critical_section && 623 try_enter_critical_section (&g_thread_global_spinlock)) 624 { 625 LeaveCriticalSection (&g_thread_global_spinlock); 626 627 g_thread_functions_for_glib_use_default.mutex_new = 628 g_mutex_new_win32_cs_impl; 629 g_thread_functions_for_glib_use_default.mutex_lock = 630 g_mutex_lock_win32_cs_impl; 631 g_thread_functions_for_glib_use_default.mutex_trylock = 632 g_mutex_trylock_win32_cs_impl; 633 g_thread_functions_for_glib_use_default.mutex_unlock = 634 g_mutex_unlock_win32_cs_impl; 635 g_thread_functions_for_glib_use_default.mutex_free = 636 g_mutex_free_win32_cs_impl; 637 } 638 } 639 } 640