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 33struct DBusCondVar { 34 DBusList *list; /**< list thread-local-stored events waiting on the cond variable */ 35 CRITICAL_SECTION lock; /**< lock protecting the list */ 36}; 37 38static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES; 39 40 41static HMODULE dbus_dll_hmodule; 42 43void * 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 55BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID); 56 57/* We need this to free the TLS events on thread exit */ 58BOOL WINAPI 59DllMain (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 93DBusCMutex * 94_dbus_platform_cmutex_new (void) 95{ 96 HANDLE handle; 97 handle = CreateMutex (NULL, FALSE, NULL); 98 return (DBusCMutex *) handle; 99} 100 101DBusRMutex * 102_dbus_platform_rmutex_new (void) 103{ 104 HANDLE handle; 105 handle = CreateMutex (NULL, FALSE, NULL); 106 return (DBusRMutex *) handle; 107} 108 109void 110_dbus_platform_cmutex_free (DBusCMutex *mutex) 111{ 112 CloseHandle ((HANDLE *) mutex); 113} 114 115void 116_dbus_platform_rmutex_free (DBusRMutex *mutex) 117{ 118 CloseHandle ((HANDLE *) mutex); 119} 120 121void 122_dbus_platform_cmutex_lock (DBusCMutex *mutex) 123{ 124 WaitForSingleObject ((HANDLE *) mutex, INFINITE); 125} 126 127void 128_dbus_platform_rmutex_lock (DBusRMutex *mutex) 129{ 130 WaitForSingleObject ((HANDLE *) mutex, INFINITE); 131} 132 133void 134_dbus_platform_cmutex_unlock (DBusCMutex *mutex) 135{ 136 ReleaseMutex ((HANDLE *) mutex); 137} 138 139void 140_dbus_platform_rmutex_unlock (DBusRMutex *mutex) 141{ 142 ReleaseMutex ((HANDLE *) mutex); 143} 144 145DBusCondVar * 146_dbus_platform_condvar_new (void) 147{ 148 DBusCondVar *cond; 149 150 cond = dbus_new (DBusCondVar, 1); 151 if (cond == NULL) 152 return NULL; 153 154 cond->list = NULL; 155 156 InitializeCriticalSection (&cond->lock); 157 return cond; 158} 159 160void 161_dbus_platform_condvar_free (DBusCondVar *cond) 162{ 163 DeleteCriticalSection (&cond->lock); 164 _dbus_list_clear (&cond->list); 165 dbus_free (cond); 166} 167 168static dbus_bool_t 169_dbus_condvar_wait_win32 (DBusCondVar *cond, 170 DBusCMutex *mutex, 171 int milliseconds) 172{ 173 DWORD retval; 174 dbus_bool_t ret; 175 HANDLE event = TlsGetValue (dbus_cond_event_tls); 176 177 if (!event) 178 { 179 event = CreateEvent (0, FALSE, FALSE, NULL); 180 if (event == 0) 181 return FALSE; 182 TlsSetValue (dbus_cond_event_tls, event); 183 } 184 185 EnterCriticalSection (&cond->lock); 186 187 /* The event must not be signaled. Check this */ 188 _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT); 189 190 ret = _dbus_list_append (&cond->list, event); 191 192 LeaveCriticalSection (&cond->lock); 193 194 if (!ret) 195 return FALSE; /* Prepend failed */ 196 197 _dbus_platform_cmutex_unlock (mutex); 198 retval = WaitForSingleObject (event, milliseconds); 199 _dbus_platform_cmutex_lock (mutex); 200 201 if (retval == WAIT_TIMEOUT) 202 { 203 EnterCriticalSection (&cond->lock); 204 _dbus_list_remove (&cond->list, event); 205 206 /* In the meantime we could have been signaled, so we must again 207 * wait for the signal, this time with no timeout, to reset 208 * it. retval is set again to honour the late arrival of the 209 * signal */ 210 retval = WaitForSingleObject (event, 0); 211 212 LeaveCriticalSection (&cond->lock); 213 } 214 215#ifndef DBUS_DISABLE_ASSERT 216 EnterCriticalSection (&cond->lock); 217 218 /* Now event must not be inside the array, check this */ 219 _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE); 220 221 LeaveCriticalSection (&cond->lock); 222#endif /* !G_DISABLE_ASSERT */ 223 224 return retval != WAIT_TIMEOUT; 225} 226 227void 228_dbus_platform_condvar_wait (DBusCondVar *cond, 229 DBusCMutex *mutex) 230{ 231 _dbus_condvar_wait_win32 (cond, mutex, INFINITE); 232} 233 234dbus_bool_t 235_dbus_platform_condvar_wait_timeout (DBusCondVar *cond, 236 DBusCMutex *mutex, 237 int timeout_milliseconds) 238{ 239 return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds); 240} 241 242void 243_dbus_platform_condvar_wake_one (DBusCondVar *cond) 244{ 245 EnterCriticalSection (&cond->lock); 246 247 if (cond->list != NULL) 248 { 249 SetEvent (_dbus_list_pop_first (&cond->list)); 250 /* Avoid live lock by pushing the waiter to the mutex lock 251 instruction, which is fair. If we don't do this, we could 252 acquire the condition variable again before the waiter has a 253 chance itself, leading to starvation. */ 254 Sleep (0); 255 } 256 LeaveCriticalSection (&cond->lock); 257} 258 259dbus_bool_t 260_dbus_threads_init_platform_specific (void) 261{ 262 /* We reuse this over several generations, because we can't 263 * free the events once they are in use 264 */ 265 if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) 266 { 267 dbus_cond_event_tls = TlsAlloc (); 268 if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) 269 return FALSE; 270 } 271 272 return dbus_threads_init (NULL); 273} 274 275