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