D-Bus: dbus-sysdeps-thread-win.c Source File

D-Bus 1.10.22

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