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