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