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