D-Bus  1.8.16
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 static dbus_bool_t global_init_done = FALSE;
34 static CRITICAL_SECTION init_lock;
35 
36 /* Called from C++ code in dbus-init-win.cpp. */
37 void
38 _dbus_threads_windows_init_global (void)
39 {
40  /* this ensures that the object that acts as our global constructor
41  * actually gets linked in when we're linked statically */
42  _dbus_threads_windows_ensure_ctor_linked ();
43 
44  InitializeCriticalSection (&init_lock);
45  global_init_done = TRUE;
46 }
47 
48 struct DBusCondVar {
50  CRITICAL_SECTION lock;
51 };
52 
53 static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;
54 
55 
56 static HMODULE dbus_dll_hmodule;
57 
58 void *
59 _dbus_win_get_dll_hmodule (void)
60 {
61  return dbus_dll_hmodule;
62 }
63 
64 #ifdef DBUS_WINCE
65 #define hinst_t HANDLE
66 #else
67 #define hinst_t HINSTANCE
68 #endif
69 
70 BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
71 
72 /* We need this to free the TLS events on thread exit */
73 BOOL WINAPI
74 DllMain (hinst_t hinstDLL,
75  DWORD fdwReason,
76  LPVOID lpvReserved)
77 {
78  HANDLE event;
79  switch (fdwReason)
80  {
81  case DLL_PROCESS_ATTACH:
82  dbus_dll_hmodule = hinstDLL;
83  break;
84  case DLL_THREAD_DETACH:
85  if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
86  {
87  event = TlsGetValue(dbus_cond_event_tls);
88  CloseHandle (event);
89  TlsSetValue(dbus_cond_event_tls, NULL);
90  }
91  break;
92  case DLL_PROCESS_DETACH:
93  if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
94  {
95  event = TlsGetValue(dbus_cond_event_tls);
96  CloseHandle (event);
97  TlsSetValue(dbus_cond_event_tls, NULL);
98 
99  TlsFree(dbus_cond_event_tls);
100  }
101  break;
102  default:
103  break;
104  }
105  return TRUE;
106 }
107 
108 DBusCMutex *
109 _dbus_platform_cmutex_new (void)
110 {
111  HANDLE handle;
112  handle = CreateMutex (NULL, FALSE, NULL);
113  return (DBusCMutex *) handle;
114 }
115 
116 DBusRMutex *
117 _dbus_platform_rmutex_new (void)
118 {
119  HANDLE handle;
120  handle = CreateMutex (NULL, FALSE, NULL);
121  return (DBusRMutex *) handle;
122 }
123 
124 void
125 _dbus_platform_cmutex_free (DBusCMutex *mutex)
126 {
127  CloseHandle ((HANDLE *) mutex);
128 }
129 
130 void
131 _dbus_platform_rmutex_free (DBusRMutex *mutex)
132 {
133  CloseHandle ((HANDLE *) mutex);
134 }
135 
136 void
137 _dbus_platform_cmutex_lock (DBusCMutex *mutex)
138 {
139  WaitForSingleObject ((HANDLE *) mutex, INFINITE);
140 }
141 
142 void
143 _dbus_platform_rmutex_lock (DBusRMutex *mutex)
144 {
145  WaitForSingleObject ((HANDLE *) mutex, INFINITE);
146 }
147 
148 void
149 _dbus_platform_cmutex_unlock (DBusCMutex *mutex)
150 {
151  ReleaseMutex ((HANDLE *) mutex);
152 }
153 
154 void
155 _dbus_platform_rmutex_unlock (DBusRMutex *mutex)
156 {
157  ReleaseMutex ((HANDLE *) mutex);
158 }
159 
160 DBusCondVar *
161 _dbus_platform_condvar_new (void)
162 {
163  DBusCondVar *cond;
164 
165  cond = dbus_new (DBusCondVar, 1);
166  if (cond == NULL)
167  return NULL;
168 
169  cond->list = NULL;
170 
171  InitializeCriticalSection (&cond->lock);
172  return cond;
173 }
174 
175 void
176 _dbus_platform_condvar_free (DBusCondVar *cond)
177 {
178  DeleteCriticalSection (&cond->lock);
179  _dbus_list_clear (&cond->list);
180  dbus_free (cond);
181 }
182 
183 static dbus_bool_t
184 _dbus_condvar_wait_win32 (DBusCondVar *cond,
185  DBusCMutex *mutex,
186  int milliseconds)
187 {
188  DWORD retval;
189  dbus_bool_t ret;
190  HANDLE event = TlsGetValue (dbus_cond_event_tls);
191 
192  if (!event)
193  {
194  event = CreateEvent (0, FALSE, FALSE, NULL);
195  if (event == 0)
196  return FALSE;
197  TlsSetValue (dbus_cond_event_tls, event);
198  }
199 
200  EnterCriticalSection (&cond->lock);
201 
202  /* The event must not be signaled. Check this */
203  _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);
204 
205  ret = _dbus_list_append (&cond->list, event);
206 
207  LeaveCriticalSection (&cond->lock);
208 
209  if (!ret)
210  return FALSE; /* Prepend failed */
211 
212  _dbus_platform_cmutex_unlock (mutex);
213  retval = WaitForSingleObject (event, milliseconds);
214  _dbus_platform_cmutex_lock (mutex);
215 
216  if (retval == WAIT_TIMEOUT)
217  {
218  EnterCriticalSection (&cond->lock);
219  _dbus_list_remove (&cond->list, event);
220 
221  /* In the meantime we could have been signaled, so we must again
222  * wait for the signal, this time with no timeout, to reset
223  * it. retval is set again to honour the late arrival of the
224  * signal */
225  retval = WaitForSingleObject (event, 0);
226 
227  LeaveCriticalSection (&cond->lock);
228  }
229 
230 #ifndef DBUS_DISABLE_ASSERT
231  EnterCriticalSection (&cond->lock);
232 
233  /* Now event must not be inside the array, check this */
234  _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE);
235 
236  LeaveCriticalSection (&cond->lock);
237 #endif /* !G_DISABLE_ASSERT */
238 
239  return retval != WAIT_TIMEOUT;
240 }
241 
242 void
243 _dbus_platform_condvar_wait (DBusCondVar *cond,
244  DBusCMutex *mutex)
245 {
246  _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
247 }
248 
250 _dbus_platform_condvar_wait_timeout (DBusCondVar *cond,
251  DBusCMutex *mutex,
252  int timeout_milliseconds)
253 {
254  return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds);
255 }
256 
257 void
258 _dbus_platform_condvar_wake_one (DBusCondVar *cond)
259 {
260  EnterCriticalSection (&cond->lock);
261 
262  if (cond->list != NULL)
263  {
264  SetEvent (_dbus_list_pop_first (&cond->list));
265  /* Avoid live lock by pushing the waiter to the mutex lock
266  instruction, which is fair. If we don't do this, we could
267  acquire the condition variable again before the waiter has a
268  chance itself, leading to starvation. */
269  Sleep (0);
270  }
271  LeaveCriticalSection (&cond->lock);
272 }
273 
276 {
277  /* We reuse this over several generations, because we can't
278  * free the events once they are in use
279  */
280  if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
281  {
282  dbus_cond_event_tls = TlsAlloc ();
283  if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
284  return FALSE;
285  }
286 
287  return TRUE;
288 }
289 
290 void
292 {
293  _dbus_assert (global_init_done);
294  EnterCriticalSection (&init_lock);
295 }
296 
297 void
299 {
300  _dbus_assert (global_init_done);
301  LeaveCriticalSection (&init_lock);
302 }
#define NULL
A null pointer, defined appropriately for C or C++.
void dbus_free(void *memory)
Frees a block of memory previously allocated by dbus_malloc() or dbus_malloc0().
Definition: dbus-memory.c:701
#define dbus_new(type, count)
Safe macro for using dbus_malloc().
Definition: dbus-memory.h:58
#define _dbus_assert(condition)
Aborts with an error message if the condition is false.
dbus_bool_t _dbus_threads_init_platform_specific(void)
Initialize threads as in dbus_threads_init_default(), appropriately for the platform.
dbus_bool_t _dbus_list_remove(DBusList **list, void *data)
Removes a value from the list.
Definition: dbus-list.c:415
DBusList * list
list thread-local-stored events waiting on the cond variable
void _dbus_threads_unlock_platform_specific(void)
Undo _dbus_threads_lock_platform_specific().
void _dbus_threads_lock_platform_specific(void)
Lock a static mutex used to protect _dbus_threads_init_platform_specific().
dbus_uint32_t dbus_bool_t
A boolean, valid values are TRUE and FALSE.
Definition: dbus-types.h:35
dbus_bool_t _dbus_list_append(DBusList **list, void *data)
Appends a value to the list.
Definition: dbus-list.c:270
void * _dbus_list_pop_first(DBusList **list)
Removes the first value in the list and returns it.
Definition: dbus-list.c:649
#define TRUE
Expands to "1".
A node in a linked list.
Definition: dbus-list.h:34
#define FALSE
Expands to "0".
CRITICAL_SECTION lock
lock protecting the list
void _dbus_list_clear(DBusList **list)
Frees all links in the list and sets the list head to NULL.
Definition: dbus-list.c:542