D-Bus  1.4.10
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 {
35  CRITICAL_SECTION lock;
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 
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