D-Bus 1.15.4
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 * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 *
24 */
25
26#include <config.h>
27#include "dbus-internals.h"
28#include "dbus-sysdeps.h"
29#include "dbus-sysdeps-win.h"
30#include "dbus-threads.h"
31#include "dbus-list.h"
32
33#include <stdio.h>
34
35#include <windows.h>
36
37#ifdef DBUS_DISABLE_ASSERT
38#define THREAD_CHECK_TRUE(func_name, result_or_call) \
39 do { if (!(result_or_call)) { /* ignore */ } } while (0)
40#else
41#define THREAD_CHECK_TRUE(func_name, result_or_call) do { \
42 if (!(result_or_call)) { \
43 _dbus_warn_check_failed ("thread function %s failed (windows error code=%ld) in %s", \
44 func_name, GetLastError (), _DBUS_FUNCTION_NAME); \
45 } \
46} while (0)
47#endif /* !DBUS_DISABLE_ASSERT */
48
49/* Protected by DllMain lock, effectively */
50static dbus_bool_t global_init_done = FALSE;
51static CRITICAL_SECTION init_lock;
52
53/* Called from C++ code in dbus-init-win.cpp. */
54void
55_dbus_threads_windows_init_global (void)
56{
57 /* this ensures that the object that acts as our global constructor
58 * actually gets linked in when we're linked statically */
59 _dbus_threads_windows_ensure_ctor_linked ();
60
61 InitializeCriticalSection (&init_lock);
62 global_init_done = TRUE;
63}
64
65struct DBusCondVar {
67 CRITICAL_SECTION lock;
68};
69
70static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;
71
72/* Protected by DllMain lock, effectively */
73static HMODULE dbus_dll_hmodule;
74
75void *
76_dbus_win_get_dll_hmodule (void)
77{
78 return dbus_dll_hmodule;
79}
80
81#ifdef DBUS_WINCE
82#define hinst_t HANDLE
83#else
84#define hinst_t HINSTANCE
85#endif
86
87BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
88
89/* We need this to free the TLS events on thread exit */
90BOOL WINAPI
91DllMain (hinst_t hinstDLL,
92 DWORD fdwReason,
93 LPVOID lpvReserved)
94{
95 HANDLE event;
96 switch (fdwReason)
97 {
98 case DLL_PROCESS_ATTACH:
99 dbus_dll_hmodule = hinstDLL;
100 break;
101 case DLL_THREAD_DETACH:
102 if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
103 {
104 event = TlsGetValue(dbus_cond_event_tls);
105 CloseHandle (event);
106 TlsSetValue(dbus_cond_event_tls, NULL);
107 }
108 break;
109 case DLL_PROCESS_DETACH:
110 if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
111 {
112 event = TlsGetValue(dbus_cond_event_tls);
113 CloseHandle (event);
114 TlsSetValue(dbus_cond_event_tls, NULL);
115
116 TlsFree(dbus_cond_event_tls);
117 }
118 break;
119 default:
120 break;
121 }
122 return TRUE;
123}
124
126_dbus_platform_cmutex_new (void)
127{
128 HANDLE handle;
129 handle = CreateMutex (NULL, FALSE, NULL);
130 THREAD_CHECK_TRUE ("CreateMutex", handle);
131 return (DBusCMutex *) handle;
132}
133
135_dbus_platform_rmutex_new (void)
136{
137 HANDLE handle;
138 handle = CreateMutex (NULL, FALSE, NULL);
139 THREAD_CHECK_TRUE ("CreateMutex", handle);
140 return (DBusRMutex *) handle;
141}
142
144_dbus_win_rmutex_named_new (const char *name)
145{
146 HANDLE handle;
147 handle = CreateMutex (NULL, FALSE, name);
148 THREAD_CHECK_TRUE ("CreateMutex", handle);
149 return (DBusRMutex *) handle;
150}
151
152void
153_dbus_platform_cmutex_free (DBusCMutex *mutex)
154{
155 THREAD_CHECK_TRUE ("CloseHandle", CloseHandle ((HANDLE *) mutex));
156}
157
158void
159_dbus_platform_rmutex_free (DBusRMutex *mutex)
160{
161 THREAD_CHECK_TRUE ("CloseHandle", CloseHandle ((HANDLE *) mutex));
162}
163
164void
165_dbus_platform_cmutex_lock (DBusCMutex *mutex)
166{
167 THREAD_CHECK_TRUE ("WaitForSingleObject", WaitForSingleObject ((HANDLE *) mutex, INFINITE) == WAIT_OBJECT_0);
168}
169
170void
171_dbus_platform_rmutex_lock (DBusRMutex *mutex)
172{
173 THREAD_CHECK_TRUE ("WaitForSingleObject", WaitForSingleObject ((HANDLE *) mutex, INFINITE) == WAIT_OBJECT_0);
174}
175
176void
177_dbus_platform_cmutex_unlock (DBusCMutex *mutex)
178{
179 THREAD_CHECK_TRUE ("ReleaseMutex", ReleaseMutex ((HANDLE *) mutex));
180}
181
182void
183_dbus_platform_rmutex_unlock (DBusRMutex *mutex)
184{
185 THREAD_CHECK_TRUE ("ReleaseMutex", ReleaseMutex ((HANDLE *) mutex));
186}
187
189_dbus_platform_condvar_new (void)
190{
191 DBusCondVar *cond;
192
193 cond = dbus_new (DBusCondVar, 1);
194 if (cond == NULL)
195 return NULL;
196
197 cond->list = NULL;
198
199 InitializeCriticalSection (&cond->lock);
200 return cond;
201}
202
203void
204_dbus_platform_condvar_free (DBusCondVar *cond)
205{
206 DeleteCriticalSection (&cond->lock);
207 _dbus_list_clear (&cond->list);
208 dbus_free (cond);
209}
210
211static dbus_bool_t
212_dbus_condvar_wait_win32 (DBusCondVar *cond,
213 DBusCMutex *mutex,
214 int milliseconds)
215{
216 DWORD retval;
217 dbus_bool_t ret;
218 HANDLE event = TlsGetValue (dbus_cond_event_tls);
219
220 if (!event)
221 {
222 event = CreateEvent (0, FALSE, FALSE, NULL);
223 if (event == 0)
224 return FALSE;
225 TlsSetValue (dbus_cond_event_tls, event);
226 }
227
228 EnterCriticalSection (&cond->lock);
229
230 /* The event must not be signaled. Check this */
231 _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);
232
233 ret = _dbus_list_append (&cond->list, event);
234
235 LeaveCriticalSection (&cond->lock);
236
237 if (!ret)
238 return FALSE; /* Prepend failed */
239
240 _dbus_platform_cmutex_unlock (mutex);
241 retval = WaitForSingleObject (event, milliseconds);
242 _dbus_platform_cmutex_lock (mutex);
243
244 if (retval == WAIT_TIMEOUT)
245 {
246 EnterCriticalSection (&cond->lock);
247 _dbus_list_remove (&cond->list, event);
248
249 /* In the meantime we could have been signaled, so we must again
250 * wait for the signal, this time with no timeout, to reset
251 * it. retval is set again to honour the late arrival of the
252 * signal */
253 retval = WaitForSingleObject (event, 0);
254
255 LeaveCriticalSection (&cond->lock);
256 }
257
258#ifndef DBUS_DISABLE_ASSERT
259 EnterCriticalSection (&cond->lock);
260
261 /* Now event must not be inside the array, check this */
262 _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE);
263
264 LeaveCriticalSection (&cond->lock);
265#endif /* !G_DISABLE_ASSERT */
266
267 return retval != WAIT_TIMEOUT;
268}
269
270void
271_dbus_platform_condvar_wait (DBusCondVar *cond,
272 DBusCMutex *mutex)
273{
274 _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
275}
276
278_dbus_platform_condvar_wait_timeout (DBusCondVar *cond,
279 DBusCMutex *mutex,
280 int timeout_milliseconds)
281{
282 return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds);
283}
284
285void
286_dbus_platform_condvar_wake_one (DBusCondVar *cond)
287{
288 EnterCriticalSection (&cond->lock);
289
290 if (cond->list != NULL)
291 {
292 SetEvent (_dbus_list_pop_first (&cond->list));
293 /* Avoid live lock by pushing the waiter to the mutex lock
294 instruction, which is fair. If we don't do this, we could
295 acquire the condition variable again before the waiter has a
296 chance itself, leading to starvation. */
297 Sleep (0);
298 }
299 LeaveCriticalSection (&cond->lock);
300}
301
304{
305 /* We reuse this over several generations, because we can't
306 * free the events once they are in use
307 */
308 if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
309 {
310 dbus_cond_event_tls = TlsAlloc ();
311 if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
312 return FALSE;
313 }
314
315 return TRUE;
316}
317
318void
320{
321 _dbus_assert (global_init_done);
322 EnterCriticalSection (&init_lock);
323}
324
325void
327{
328 _dbus_assert (global_init_done);
329 LeaveCriticalSection (&init_lock);
330}
331
332#ifdef DBUS_ENABLE_VERBOSE_MODE
333void
334_dbus_print_thread (void)
335{
336 fprintf (stderr, "%lu: 0x%04lx: ", _dbus_pid_for_log (), GetCurrentThreadId ());
337}
338#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:418
void * _dbus_list_pop_first(DBusList **list)
Removes the first value in the list and returns it.
Definition: dbus-list.c:679
void _dbus_list_clear(DBusList **list)
Frees all links in the list and sets the list head to NULL.
Definition: dbus-list.c:545
dbus_bool_t _dbus_list_append(DBusList **list, void *data)
Appends a value to the list.
Definition: dbus-list.c:273
#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:694
#define dbus_new(type, count)
Safe macro for using dbus_malloc().
Definition: dbus-memory.h:59
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:37
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:37