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