Home | History | Annotate | Download | only in gthread
      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 (&current_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