D-Bus  1.10.12
dbus-sysdeps-thread-win.c
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-sysdeps-pthread.c Implements threads using Windows threads (internal to libdbus)
00003  * 
00004  * Copyright (C) 2006  Red Hat, Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  * 
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  * 
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include "dbus-internals.h"
00026 #include "dbus-sysdeps.h"
00027 #include "dbus-sysdeps-win.h"
00028 #include "dbus-threads.h"
00029 #include "dbus-list.h"
00030 
00031 #include <windows.h>
00032 
00033 static dbus_bool_t global_init_done = FALSE;
00034 static CRITICAL_SECTION init_lock;
00035 
00036 /* Called from C++ code in dbus-init-win.cpp. */
00037 void
00038 _dbus_threads_windows_init_global (void)
00039 {
00040   /* this ensures that the object that acts as our global constructor
00041    * actually gets linked in when we're linked statically */
00042   _dbus_threads_windows_ensure_ctor_linked ();
00043 
00044   InitializeCriticalSection (&init_lock);
00045   global_init_done = TRUE;
00046 }
00047 
00048 struct DBusCondVar {
00049   DBusList *list;        
00050   CRITICAL_SECTION lock; 
00051 };
00052 
00053 static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;
00054 
00055 
00056 static HMODULE dbus_dll_hmodule;
00057 
00058 void *
00059 _dbus_win_get_dll_hmodule (void)
00060 {
00061   return dbus_dll_hmodule;
00062 }
00063 
00064 #ifdef DBUS_WINCE
00065 #define hinst_t HANDLE
00066 #else
00067 #define hinst_t HINSTANCE
00068 #endif
00069 
00070 BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
00071 
00072 /* We need this to free the TLS events on thread exit */
00073 BOOL WINAPI
00074 DllMain (hinst_t hinstDLL,
00075          DWORD     fdwReason,
00076          LPVOID    lpvReserved)
00077 {
00078   HANDLE event;
00079   switch (fdwReason) 
00080     { 
00081     case DLL_PROCESS_ATTACH:
00082       dbus_dll_hmodule = hinstDLL;
00083       break;
00084     case DLL_THREAD_DETACH:
00085       if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
00086         {
00087           event = TlsGetValue(dbus_cond_event_tls);
00088           CloseHandle (event);
00089           TlsSetValue(dbus_cond_event_tls, NULL);
00090         }
00091       break;
00092     case DLL_PROCESS_DETACH: 
00093       if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
00094         {
00095           event = TlsGetValue(dbus_cond_event_tls);
00096           CloseHandle (event);
00097           TlsSetValue(dbus_cond_event_tls, NULL);
00098 
00099           TlsFree(dbus_cond_event_tls); 
00100         }
00101       break;
00102     default: 
00103       break; 
00104     }
00105   return TRUE;
00106 }
00107 
00108 DBusCMutex *
00109 _dbus_platform_cmutex_new (void)
00110 {
00111   HANDLE handle;
00112   handle = CreateMutex (NULL, FALSE, NULL);
00113   return (DBusCMutex *) handle;
00114 }
00115 
00116 DBusRMutex *
00117 _dbus_platform_rmutex_new (void)
00118 {
00119   HANDLE handle;
00120   handle = CreateMutex (NULL, FALSE, NULL);
00121   return (DBusRMutex *) handle;
00122 }
00123 
00124 void
00125 _dbus_platform_cmutex_free (DBusCMutex *mutex)
00126 {
00127   CloseHandle ((HANDLE *) mutex);
00128 }
00129 
00130 void
00131 _dbus_platform_rmutex_free (DBusRMutex *mutex)
00132 {
00133   CloseHandle ((HANDLE *) mutex);
00134 }
00135 
00136 void
00137 _dbus_platform_cmutex_lock (DBusCMutex *mutex)
00138 {
00139   WaitForSingleObject ((HANDLE *) mutex, INFINITE);
00140 }
00141 
00142 void
00143 _dbus_platform_rmutex_lock (DBusRMutex *mutex)
00144 {
00145   WaitForSingleObject ((HANDLE *) mutex, INFINITE);
00146 }
00147 
00148 void
00149 _dbus_platform_cmutex_unlock (DBusCMutex *mutex)
00150 {
00151   ReleaseMutex ((HANDLE *) mutex);
00152 }
00153 
00154 void
00155 _dbus_platform_rmutex_unlock (DBusRMutex *mutex)
00156 {
00157   ReleaseMutex ((HANDLE *) mutex);
00158 }
00159 
00160 DBusCondVar *
00161 _dbus_platform_condvar_new (void)
00162 {
00163   DBusCondVar *cond;
00164     
00165   cond = dbus_new (DBusCondVar, 1);
00166   if (cond == NULL)
00167     return NULL;
00168   
00169   cond->list = NULL;
00170   
00171   InitializeCriticalSection (&cond->lock);
00172   return cond;
00173 }
00174 
00175 void
00176 _dbus_platform_condvar_free (DBusCondVar *cond)
00177 {
00178   DeleteCriticalSection (&cond->lock);
00179   _dbus_list_clear (&cond->list);
00180   dbus_free (cond);
00181 }
00182 
00183 static dbus_bool_t
00184 _dbus_condvar_wait_win32 (DBusCondVar *cond,
00185                           DBusCMutex *mutex,
00186                           int milliseconds)
00187 {
00188   DWORD retval;
00189   dbus_bool_t ret;
00190   HANDLE event = TlsGetValue (dbus_cond_event_tls);
00191 
00192   if (!event)
00193     {
00194       event = CreateEvent (0, FALSE, FALSE, NULL);
00195       if (event == 0)
00196         return FALSE;
00197       TlsSetValue (dbus_cond_event_tls, event);
00198     }
00199 
00200   EnterCriticalSection (&cond->lock);
00201 
00202   /* The event must not be signaled. Check this */
00203   _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);
00204 
00205   ret = _dbus_list_append (&cond->list, event);
00206   
00207   LeaveCriticalSection (&cond->lock);
00208   
00209   if (!ret)
00210     return FALSE; /* Prepend failed */
00211 
00212   _dbus_platform_cmutex_unlock (mutex);
00213   retval = WaitForSingleObject (event, milliseconds);
00214   _dbus_platform_cmutex_lock (mutex);
00215   
00216   if (retval == WAIT_TIMEOUT)
00217     {
00218       EnterCriticalSection (&cond->lock);
00219       _dbus_list_remove (&cond->list, event);
00220 
00221       /* In the meantime we could have been signaled, so we must again
00222        * wait for the signal, this time with no timeout, to reset
00223        * it. retval is set again to honour the late arrival of the
00224        * signal */
00225       retval = WaitForSingleObject (event, 0);
00226 
00227       LeaveCriticalSection (&cond->lock);
00228     }
00229 
00230 #ifndef DBUS_DISABLE_ASSERT
00231   EnterCriticalSection (&cond->lock);
00232 
00233   /* Now event must not be inside the array, check this */
00234   _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE);
00235 
00236   LeaveCriticalSection (&cond->lock);
00237 #endif /* !G_DISABLE_ASSERT */
00238 
00239   return retval != WAIT_TIMEOUT;
00240 }
00241 
00242 void
00243 _dbus_platform_condvar_wait (DBusCondVar *cond,
00244                              DBusCMutex  *mutex)
00245 {
00246   _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
00247 }
00248 
00249 dbus_bool_t
00250 _dbus_platform_condvar_wait_timeout (DBusCondVar               *cond,
00251                                      DBusCMutex                *mutex,
00252                                      int                        timeout_milliseconds)
00253 {
00254   return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds);
00255 }
00256 
00257 void
00258 _dbus_platform_condvar_wake_one (DBusCondVar *cond)
00259 {
00260   EnterCriticalSection (&cond->lock);
00261   
00262   if (cond->list != NULL)
00263     {
00264       SetEvent (_dbus_list_pop_first (&cond->list));
00265       /* Avoid live lock by pushing the waiter to the mutex lock
00266          instruction, which is fair.  If we don't do this, we could
00267          acquire the condition variable again before the waiter has a
00268          chance itself, leading to starvation.  */
00269       Sleep (0);
00270     }
00271   LeaveCriticalSection (&cond->lock);
00272 }
00273 
00274 dbus_bool_t
00275 _dbus_threads_init_platform_specific (void)
00276 {
00277   /* We reuse this over several generations, because we can't
00278    * free the events once they are in use
00279    */
00280   if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
00281     {
00282       dbus_cond_event_tls = TlsAlloc ();
00283       if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
00284         return FALSE;
00285     }
00286 
00287   return TRUE;
00288 }
00289 
00290 void
00291 _dbus_threads_lock_platform_specific (void)
00292 {
00293   _dbus_assert (global_init_done);
00294   EnterCriticalSection (&init_lock);
00295 }
00296 
00297 void
00298 _dbus_threads_unlock_platform_specific (void)
00299 {
00300   _dbus_assert (global_init_done);
00301   LeaveCriticalSection (&init_lock);
00302 }