_dbus_connection_message_sent: rename to _unlocked
[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     {
223       SetEvent (_dbus_list_pop_first (&cond->list));
224       /* Avoid live lock by pushing the waiter to the mutex lock
225          instruction, which is fair.  If we don't do this, we could
226          acquire the condition variable again before the waiter has a
227          chance itself, leading to starvation.  */
228       Sleep (0);
229     }
230   LeaveCriticalSection (&cond->lock);
231 }
232
233 static void
234 _dbus_windows_condvar_wake_all (DBusCondVar *cond)
235 {
236   EnterCriticalSection (&cond->lock);
237
238   while (cond->list != NULL)
239     SetEvent (_dbus_list_pop_first (&cond->list));
240
241   if (cond->list != NULL)
242     {
243       /* Avoid live lock by pushing the waiter to the mutex lock
244          instruction, which is fair.  If we don't do this, we could
245          acquire the condition variable again before the waiter has a
246          chance itself, leading to starvation.  */
247       Sleep (0);
248     }
249
250   LeaveCriticalSection (&cond->lock);
251 }
252
253 static const DBusThreadFunctions windows_functions =
254 {
255   DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK |
256   DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK |
257   DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK |
258   DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK |
259   DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
260   DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
261   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
262   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
263   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK|
264   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
265   _dbus_windows_mutex_new,
266   _dbus_windows_mutex_free,
267   _dbus_windows_mutex_lock,
268   _dbus_windows_mutex_unlock,
269   _dbus_windows_condvar_new,
270   _dbus_windows_condvar_free,
271   _dbus_windows_condvar_wait,
272   _dbus_windows_condvar_wait_timeout,
273   _dbus_windows_condvar_wake_one,
274   _dbus_windows_condvar_wake_all
275 };
276
277 dbus_bool_t
278 _dbus_threads_init_platform_specific (void)
279 {
280   /* We reuse this over several generations, because we can't
281    * free the events once they are in use
282    */
283   if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
284     {
285       dbus_cond_event_tls = TlsAlloc ();
286       if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
287         return FALSE;
288     }
289
290   return dbus_threads_init (&windows_functions);
291 }
292