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 struct DBusCondVar { 34 DBusList *list; /**< list thread-local-stored events waiting on the cond variable */ 35 CRITICAL_SECTION lock; /**< lock protecting the list */ 36 }; 37 38 static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES; 39 40 41 static HMODULE dbus_dll_hmodule; 42 43 void * 44 _dbus_win_get_dll_hmodule (void) 45 { 46 return dbus_dll_hmodule; 47 } 48 49 #ifdef DBUS_WINCE 50 #define hinst_t HANDLE 51 #else 52 #define hinst_t HINSTANCE 53 #endif 54 55 BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID); 56 57 /* We need this to free the TLS events on thread exit */ 58 BOOL WINAPI 59 DllMain (hinst_t hinstDLL, 60 DWORD fdwReason, 61 LPVOID lpvReserved) 62 { 63 HANDLE event; 64 switch (fdwReason) 65 { 66 case DLL_PROCESS_ATTACH: 67 dbus_dll_hmodule = hinstDLL; 68 break; 69 case DLL_THREAD_DETACH: 70 if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES) 71 { 72 event = TlsGetValue(dbus_cond_event_tls); 73 CloseHandle (event); 74 TlsSetValue(dbus_cond_event_tls, NULL); 75 } 76 break; 77 case DLL_PROCESS_DETACH: 78 if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES) 79 { 80 event = TlsGetValue(dbus_cond_event_tls); 81 CloseHandle (event); 82 TlsSetValue(dbus_cond_event_tls, NULL); 83 84 TlsFree(dbus_cond_event_tls); 85 } 86 break; 87 default: 88 break; 89 } 90 return TRUE; 91 } 92 93 static DBusMutex* 94 _dbus_windows_mutex_new (void) 95 { 96 HANDLE handle; 97 handle = CreateMutex (NULL, FALSE, NULL); 98 return (DBusMutex *) handle; 99 } 100 101 static void 102 _dbus_windows_mutex_free (DBusMutex *mutex) 103 { 104 CloseHandle ((HANDLE *) mutex); 105 } 106 107 static dbus_bool_t 108 _dbus_windows_mutex_lock (DBusMutex *mutex) 109 { 110 return WaitForSingleObject ((HANDLE *) mutex, INFINITE) != WAIT_FAILED; 111 } 112 113 static dbus_bool_t 114 _dbus_windows_mutex_unlock (DBusMutex *mutex) 115 { 116 return ReleaseMutex ((HANDLE *) mutex) != 0; 117 } 118 119 static DBusCondVar * 120 _dbus_windows_condvar_new (void) 121 { 122 DBusCondVar *cond; 123 124 cond = dbus_new (DBusCondVar, 1); 125 if (cond == NULL) 126 return NULL; 127 128 cond->list = NULL; 129 130 InitializeCriticalSection (&cond->lock); 131 return (DBusCondVar *) cond; 132 } 133 134 static void 135 _dbus_windows_condvar_free (DBusCondVar *cond) 136 { 137 DeleteCriticalSection (&cond->lock); 138 _dbus_list_clear (&cond->list); 139 dbus_free (cond); 140 } 141 142 static dbus_bool_t 143 _dbus_condvar_wait_win32 (DBusCondVar *cond, 144 DBusMutex *mutex, 145 int milliseconds) 146 { 147 DWORD retval; 148 dbus_bool_t ret; 149 HANDLE event = TlsGetValue (dbus_cond_event_tls); 150 151 if (!event) 152 { 153 event = CreateEvent (0, FALSE, FALSE, NULL); 154 if (event == 0) 155 return FALSE; 156 TlsSetValue (dbus_cond_event_tls, event); 157 } 158 159 EnterCriticalSection (&cond->lock); 160 161 /* The event must not be signaled. Check this */ 162 _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT); 163 164 ret = _dbus_list_append (&cond->list, event); 165 166 LeaveCriticalSection (&cond->lock); 167 168 if (!ret) 169 return FALSE; /* Prepend failed */ 170 171 _dbus_mutex_unlock (mutex); 172 retval = WaitForSingleObject (event, milliseconds); 173 _dbus_mutex_lock (mutex); 174 175 if (retval == WAIT_TIMEOUT) 176 { 177 EnterCriticalSection (&cond->lock); 178 _dbus_list_remove (&cond->list, event); 179 180 /* In the meantime we could have been signaled, so we must again 181 * wait for the signal, this time with no timeout, to reset 182 * it. retval is set again to honour the late arrival of the 183 * signal */ 184 retval = WaitForSingleObject (event, 0); 185 186 LeaveCriticalSection (&cond->lock); 187 } 188 189 #ifndef DBUS_DISABLE_ASSERT 190 EnterCriticalSection (&cond->lock); 191 192 /* Now event must not be inside the array, check this */ 193 _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE); 194 195 LeaveCriticalSection (&cond->lock); 196 #endif /* !G_DISABLE_ASSERT */ 197 198 return retval != WAIT_TIMEOUT; 199 } 200 201 static void 202 _dbus_windows_condvar_wait (DBusCondVar *cond, 203 DBusMutex *mutex) 204 { 205 _dbus_condvar_wait_win32 (cond, mutex, INFINITE); 206 } 207 208 static dbus_bool_t 209 _dbus_windows_condvar_wait_timeout (DBusCondVar *cond, 210 DBusMutex *mutex, 211 int timeout_milliseconds) 212 { 213 return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds); 214 } 215 216 static void 217 _dbus_windows_condvar_wake_one (DBusCondVar *cond) 218 { 219 EnterCriticalSection (&cond->lock); 220 221 if (cond->list != NULL) 222 SetEvent (_dbus_list_pop_first (&cond->list)); 223 224 LeaveCriticalSection (&cond->lock); 225 } 226 227 static void 228 _dbus_windows_condvar_wake_all (DBusCondVar *cond) 229 { 230 EnterCriticalSection (&cond->lock); 231 232 while (cond->list != NULL) 233 SetEvent (_dbus_list_pop_first (&cond->list)); 234 235 LeaveCriticalSection (&cond->lock); 236 } 237 238 static const DBusThreadFunctions windows_functions = 239 { 240 DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK | 241 DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK | 242 DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK | 243 DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK | 244 DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | 245 DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | 246 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | 247 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | 248 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| 249 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, 250 _dbus_windows_mutex_new, 251 _dbus_windows_mutex_free, 252 _dbus_windows_mutex_lock, 253 _dbus_windows_mutex_unlock, 254 _dbus_windows_condvar_new, 255 _dbus_windows_condvar_free, 256 _dbus_windows_condvar_wait, 257 _dbus_windows_condvar_wait_timeout, 258 _dbus_windows_condvar_wake_one, 259 _dbus_windows_condvar_wake_all 260 }; 261 262 dbus_bool_t 263 _dbus_threads_init_platform_specific (void) 264 { 265 /* We reuse this over several generations, because we can't 266 * free the events once they are in use 267 */ 268 if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) 269 { 270 dbus_cond_event_tls = TlsAlloc (); 271 if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) 272 return FALSE; 273 } 274 275 return dbus_threads_init (&windows_functions); 276 } 277 278