D-Bus 1.15.0
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 */
48static dbus_bool_t global_init_done = FALSE;
49static CRITICAL_SECTION init_lock;
50
51/* Called from C++ code in dbus-init-win.cpp. */
52void
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
63struct DBusCondVar {
65 CRITICAL_SECTION lock;
66};
67
68static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;
69
70/* Protected by DllMain lock, effectively */
71static HMODULE dbus_dll_hmodule;
72
73void *
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
85BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
86
87/* We need this to free the TLS events on thread exit */
88BOOL WINAPI
89DllMain (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
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
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
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
150void
151_dbus_platform_cmutex_free (DBusCMutex *mutex)
152{
153 THREAD_CHECK_TRUE ("CloseHandle", CloseHandle ((HANDLE *) mutex));
154}
155
156void
157_dbus_platform_rmutex_free (DBusRMutex *mutex)
158{
159 THREAD_CHECK_TRUE ("CloseHandle", CloseHandle ((HANDLE *) mutex));
160}
161
162void
163_dbus_platform_cmutex_lock (DBusCMutex *mutex)
164{
165 THREAD_CHECK_TRUE ("WaitForSingleObject", WaitForSingleObject ((HANDLE *) mutex, INFINITE) == WAIT_OBJECT_0);
166}
167
168void
169_dbus_platform_rmutex_lock (DBusRMutex *mutex)
170{
171 THREAD_CHECK_TRUE ("WaitForSingleObject", WaitForSingleObject ((HANDLE *) mutex, INFINITE) == WAIT_OBJECT_0);
172}
173
174void
175_dbus_platform_cmutex_unlock (DBusCMutex *mutex)
176{
177 THREAD_CHECK_TRUE ("ReleaseMutex", ReleaseMutex ((HANDLE *) mutex));
178}
179
180void
181_dbus_platform_rmutex_unlock (DBusRMutex *mutex)
182{
183 THREAD_CHECK_TRUE ("ReleaseMutex", ReleaseMutex ((HANDLE *) mutex));
184}
185
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
201void
202_dbus_platform_condvar_free (DBusCondVar *cond)
203{
204 DeleteCriticalSection (&cond->lock);
205 _dbus_list_clear (&cond->list);
206 dbus_free (cond);
207}
208
209static 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
268void
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
283void
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
316void
318{
319 _dbus_assert (global_init_done);
320 EnterCriticalSection (&init_lock);
321}
322
323void
325{
326 _dbus_assert (global_init_done);
327 LeaveCriticalSection (&init_lock);
328}
329
330#ifdef DBUS_ENABLE_VERBOSE_MODE
331void
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_pop_first(DBusList **list)
Removes the first value in the list and returns it.
Definition: dbus-list.c:677
void _dbus_list_clear(DBusList **list)
Frees all links in the list and sets the list head to NULL.
Definition: dbus-list.c:543
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