Enable launchd.
[platform/upstream/dbus.git] / dbus / dbus-sysdeps-thread-win.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-sysdeps-pthread.c Implements threads using Windows threads (internal to libdbus)
3  * 
4  * Copyright (C) 2006  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #include <config.h>
25 #include "dbus-internals.h"
26 #include "dbus-sysdeps.h"
27 #include "dbus-sysdeps-win.h"
28 #include "dbus-threads.h"
29 #include "dbus-list.h"
30
31 #include <windows.h>
32
33 struct DBusCondVar {
34   DBusList *list;        /**< list thread-local-stored events waiting on the cond variable */
35   CRITICAL_SECTION lock; /**< lock protecting the list */
36 };
37
38 static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;
39
40
41 static HMODULE dbus_dll_hmodule;
42
43 void *
44 _dbus_win_get_dll_hmodule (void)
45 {
46   return dbus_dll_hmodule;
47 }
48
49 #ifdef DBUS_WINCE
50 #define hinst_t HANDLE
51 #else
52 #define hinst_t HINSTANCE
53 #endif
54
55 BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
56
57 /* We need this to free the TLS events on thread exit */
58 BOOL WINAPI
59 DllMain (hinst_t hinstDLL,
60          DWORD     fdwReason,
61          LPVOID    lpvReserved)
62 {
63   HANDLE event;
64   switch (fdwReason) 
65     { 
66     case DLL_PROCESS_ATTACH:
67       dbus_dll_hmodule = hinstDLL;
68       break;
69     case DLL_THREAD_DETACH:
70       if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
71         {
72           event = TlsGetValue(dbus_cond_event_tls);
73           CloseHandle (event);
74           TlsSetValue(dbus_cond_event_tls, NULL);
75         }
76       break;
77     case DLL_PROCESS_DETACH: 
78       if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
79         {
80           event = TlsGetValue(dbus_cond_event_tls);
81           CloseHandle (event);
82           TlsSetValue(dbus_cond_event_tls, NULL);
83
84           TlsFree(dbus_cond_event_tls); 
85         }
86       break;
87     default: 
88       break; 
89     }
90   return TRUE;
91 }
92
93 static DBusMutex*
94 _dbus_windows_mutex_new (void)
95 {
96   HANDLE handle;
97   handle = CreateMutex (NULL, FALSE, NULL);
98   return (DBusMutex *) handle;
99 }
100
101 static void
102 _dbus_windows_mutex_free (DBusMutex *mutex)
103 {
104   CloseHandle ((HANDLE *) mutex);
105 }
106
107 static dbus_bool_t
108 _dbus_windows_mutex_lock (DBusMutex *mutex)
109 {
110   return WaitForSingleObject ((HANDLE *) mutex, INFINITE) != WAIT_FAILED;
111 }
112
113 static dbus_bool_t
114 _dbus_windows_mutex_unlock (DBusMutex *mutex)
115 {
116   return ReleaseMutex ((HANDLE *) mutex) != 0;
117 }
118
119 static DBusCondVar *
120 _dbus_windows_condvar_new (void)
121 {
122   DBusCondVar *cond;
123     
124   cond = dbus_new (DBusCondVar, 1);
125   if (cond == NULL)
126     return NULL;
127   
128   cond->list = NULL;
129   
130   InitializeCriticalSection (&cond->lock);
131   return (DBusCondVar *) cond;
132 }
133
134 static void
135 _dbus_windows_condvar_free (DBusCondVar *cond)
136 {
137   DeleteCriticalSection (&cond->lock);
138   _dbus_list_clear (&cond->list);
139   dbus_free (cond);
140 }
141
142 static dbus_bool_t
143 _dbus_condvar_wait_win32 (DBusCondVar *cond,
144                           DBusMutex *mutex,
145                           int milliseconds)
146 {
147   DWORD retval;
148   dbus_bool_t ret;
149   HANDLE event = TlsGetValue (dbus_cond_event_tls);
150
151   if (!event)
152     {
153       event = CreateEvent (0, FALSE, FALSE, NULL);
154       if (event == 0)
155         return FALSE;
156       TlsSetValue (dbus_cond_event_tls, event);
157     }
158
159   EnterCriticalSection (&cond->lock);
160
161   /* The event must not be signaled. Check this */
162   _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);
163
164   ret = _dbus_list_append (&cond->list, event);
165   
166   LeaveCriticalSection (&cond->lock);
167   
168   if (!ret)
169     return FALSE; /* Prepend failed */
170
171   _dbus_mutex_unlock (mutex);
172   retval = WaitForSingleObject (event, milliseconds);
173   _dbus_mutex_lock (mutex);
174   
175   if (retval == WAIT_TIMEOUT)
176     {
177       EnterCriticalSection (&cond->lock);
178       _dbus_list_remove (&cond->list, event);
179
180       /* In the meantime we could have been signaled, so we must again
181        * wait for the signal, this time with no timeout, to reset
182        * it. retval is set again to honour the late arrival of the
183        * signal */
184       retval = WaitForSingleObject (event, 0);
185
186       LeaveCriticalSection (&cond->lock);
187     }
188
189 #ifndef DBUS_DISABLE_ASSERT
190   EnterCriticalSection (&cond->lock);
191
192   /* Now event must not be inside the array, check this */
193   _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE);
194
195   LeaveCriticalSection (&cond->lock);
196 #endif /* !G_DISABLE_ASSERT */
197
198   return retval != WAIT_TIMEOUT;
199 }
200
201 static void
202 _dbus_windows_condvar_wait (DBusCondVar *cond,
203                             DBusMutex   *mutex)
204 {
205   _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
206 }
207
208 static dbus_bool_t
209 _dbus_windows_condvar_wait_timeout (DBusCondVar               *cond,
210                                      DBusMutex                 *mutex,
211                                      int                        timeout_milliseconds)
212 {
213   return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds);
214 }
215
216 static void
217 _dbus_windows_condvar_wake_one (DBusCondVar *cond)
218 {
219   EnterCriticalSection (&cond->lock);
220   
221   if (cond->list != NULL)
222     SetEvent (_dbus_list_pop_first (&cond->list));
223     
224   LeaveCriticalSection (&cond->lock);
225 }
226
227 static void
228 _dbus_windows_condvar_wake_all (DBusCondVar *cond)
229 {
230   EnterCriticalSection (&cond->lock);
231
232   while (cond->list != NULL)
233     SetEvent (_dbus_list_pop_first (&cond->list));
234   
235   LeaveCriticalSection (&cond->lock);
236 }
237
238 static const DBusThreadFunctions windows_functions =
239 {
240   DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK |
241   DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK |
242   DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK |
243   DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK |
244   DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
245   DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
246   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
247   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
248   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK|
249   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
250   _dbus_windows_mutex_new,
251   _dbus_windows_mutex_free,
252   _dbus_windows_mutex_lock,
253   _dbus_windows_mutex_unlock,
254   _dbus_windows_condvar_new,
255   _dbus_windows_condvar_free,
256   _dbus_windows_condvar_wait,
257   _dbus_windows_condvar_wait_timeout,
258   _dbus_windows_condvar_wake_one,
259   _dbus_windows_condvar_wake_all
260 };
261
262 dbus_bool_t
263 _dbus_threads_init_platform_specific (void)
264 {
265   /* We reuse this over several generations, because we can't
266    * free the events once they are in use
267    */
268   if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
269     {
270       dbus_cond_event_tls = TlsAlloc ();
271       if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
272         return FALSE;
273     }
274
275   return dbus_threads_init (&windows_functions);
276 }
277