D-Bus  1.4.18
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 struct DBusCondVar {
00034   DBusList *list;        
00035   CRITICAL_SECTION lock; 
00036 };
00037 
00038 static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;
00039 
00040 
00041 static HMODULE dbus_dll_hmodule;
00042 
00043 void *
00044 _dbus_win_get_dll_hmodule (void)
00045 {
00046   return dbus_dll_hmodule;
00047 }
00048 
00049 #ifdef DBUS_WINCE
00050 #define hinst_t HANDLE
00051 #else
00052 #define hinst_t HINSTANCE
00053 #endif
00054 
00055 BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
00056 
00057 /* We need this to free the TLS events on thread exit */
00058 BOOL WINAPI
00059 DllMain (hinst_t hinstDLL,
00060          DWORD     fdwReason,
00061          LPVOID    lpvReserved)
00062 {
00063   HANDLE event;
00064   switch (fdwReason) 
00065     { 
00066     case DLL_PROCESS_ATTACH:
00067       dbus_dll_hmodule = hinstDLL;
00068       break;
00069     case DLL_THREAD_DETACH:
00070       if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
00071         {
00072           event = TlsGetValue(dbus_cond_event_tls);
00073           CloseHandle (event);
00074           TlsSetValue(dbus_cond_event_tls, NULL);
00075         }
00076       break;
00077     case DLL_PROCESS_DETACH: 
00078       if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
00079         {
00080           event = TlsGetValue(dbus_cond_event_tls);
00081           CloseHandle (event);
00082           TlsSetValue(dbus_cond_event_tls, NULL);
00083 
00084           TlsFree(dbus_cond_event_tls); 
00085         }
00086       break;
00087     default: 
00088       break; 
00089     }
00090   return TRUE;
00091 }
00092 
00093 static DBusMutex*
00094 _dbus_windows_mutex_new (void)
00095 {
00096   HANDLE handle;
00097   handle = CreateMutex (NULL, FALSE, NULL);
00098   return (DBusMutex *) handle;
00099 }
00100 
00101 static void
00102 _dbus_windows_mutex_free (DBusMutex *mutex)
00103 {
00104   CloseHandle ((HANDLE *) mutex);
00105 }
00106 
00107 static dbus_bool_t
00108 _dbus_windows_mutex_lock (DBusMutex *mutex)
00109 {
00110   return WaitForSingleObject ((HANDLE *) mutex, INFINITE) != WAIT_FAILED;
00111 }
00112 
00113 static dbus_bool_t
00114 _dbus_windows_mutex_unlock (DBusMutex *mutex)
00115 {
00116   return ReleaseMutex ((HANDLE *) mutex) != 0;
00117 }
00118 
00119 static DBusCondVar *
00120 _dbus_windows_condvar_new (void)
00121 {
00122   DBusCondVar *cond;
00123     
00124   cond = dbus_new (DBusCondVar, 1);
00125   if (cond == NULL)
00126     return NULL;
00127   
00128   cond->list = NULL;
00129   
00130   InitializeCriticalSection (&cond->lock);
00131   return (DBusCondVar *) cond;
00132 }
00133 
00134 static void
00135 _dbus_windows_condvar_free (DBusCondVar *cond)
00136 {
00137   DeleteCriticalSection (&cond->lock);
00138   _dbus_list_clear (&cond->list);
00139   dbus_free (cond);
00140 }
00141 
00142 static dbus_bool_t
00143 _dbus_condvar_wait_win32 (DBusCondVar *cond,
00144                           DBusMutex *mutex,
00145                           int milliseconds)
00146 {
00147   DWORD retval;
00148   dbus_bool_t ret;
00149   HANDLE event = TlsGetValue (dbus_cond_event_tls);
00150 
00151   if (!event)
00152     {
00153       event = CreateEvent (0, FALSE, FALSE, NULL);
00154       if (event == 0)
00155         return FALSE;
00156       TlsSetValue (dbus_cond_event_tls, event);
00157     }
00158 
00159   EnterCriticalSection (&cond->lock);
00160 
00161   /* The event must not be signaled. Check this */
00162   _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);
00163 
00164   ret = _dbus_list_append (&cond->list, event);
00165   
00166   LeaveCriticalSection (&cond->lock);
00167   
00168   if (!ret)
00169     return FALSE; /* Prepend failed */
00170 
00171   _dbus_mutex_unlock (mutex);
00172   retval = WaitForSingleObject (event, milliseconds);
00173   _dbus_mutex_lock (mutex);
00174   
00175   if (retval == WAIT_TIMEOUT)
00176     {
00177       EnterCriticalSection (&cond->lock);
00178       _dbus_list_remove (&cond->list, event);
00179 
00180       /* In the meantime we could have been signaled, so we must again
00181        * wait for the signal, this time with no timeout, to reset
00182        * it. retval is set again to honour the late arrival of the
00183        * signal */
00184       retval = WaitForSingleObject (event, 0);
00185 
00186       LeaveCriticalSection (&cond->lock);
00187     }
00188 
00189 #ifndef DBUS_DISABLE_ASSERT
00190   EnterCriticalSection (&cond->lock);
00191 
00192   /* Now event must not be inside the array, check this */
00193   _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE);
00194 
00195   LeaveCriticalSection (&cond->lock);
00196 #endif /* !G_DISABLE_ASSERT */
00197 
00198   return retval != WAIT_TIMEOUT;
00199 }
00200 
00201 static void
00202 _dbus_windows_condvar_wait (DBusCondVar *cond,
00203                             DBusMutex   *mutex)
00204 {
00205   _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
00206 }
00207 
00208 static dbus_bool_t
00209 _dbus_windows_condvar_wait_timeout (DBusCondVar               *cond,
00210                                      DBusMutex                 *mutex,
00211                                      int                        timeout_milliseconds)
00212 {
00213   return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds);
00214 }
00215 
00216 static void
00217 _dbus_windows_condvar_wake_one (DBusCondVar *cond)
00218 {
00219   EnterCriticalSection (&cond->lock);
00220   
00221   if (cond->list != NULL)
00222     {
00223       SetEvent (_dbus_list_pop_first (&cond->list));
00224       /* Avoid live lock by pushing the waiter to the mutex lock
00225          instruction, which is fair.  If we don't do this, we could
00226          acquire the condition variable again before the waiter has a
00227          chance itself, leading to starvation.  */
00228       Sleep (0);
00229     }
00230   LeaveCriticalSection (&cond->lock);
00231 }
00232 
00233 static void
00234 _dbus_windows_condvar_wake_all (DBusCondVar *cond)
00235 {
00236   EnterCriticalSection (&cond->lock);
00237 
00238   while (cond->list != NULL)
00239     SetEvent (_dbus_list_pop_first (&cond->list));
00240 
00241   if (cond->list != NULL)
00242     {
00243       /* Avoid live lock by pushing the waiter to the mutex lock
00244          instruction, which is fair.  If we don't do this, we could
00245          acquire the condition variable again before the waiter has a
00246          chance itself, leading to starvation.  */
00247       Sleep (0);
00248     }
00249 
00250   LeaveCriticalSection (&cond->lock);
00251 }
00252 
00253 static const DBusThreadFunctions windows_functions =
00254 {
00255   DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK |
00256   DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK |
00257   DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK |
00258   DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK |
00259   DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
00260   DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
00261   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
00262   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
00263   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK|
00264   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
00265   _dbus_windows_mutex_new,
00266   _dbus_windows_mutex_free,
00267   _dbus_windows_mutex_lock,
00268   _dbus_windows_mutex_unlock,
00269   _dbus_windows_condvar_new,
00270   _dbus_windows_condvar_free,
00271   _dbus_windows_condvar_wait,
00272   _dbus_windows_condvar_wait_timeout,
00273   _dbus_windows_condvar_wake_one,
00274   _dbus_windows_condvar_wake_all
00275 };
00276 
00277 dbus_bool_t
00278 _dbus_threads_init_platform_specific (void)
00279 {
00280   /* We reuse this over several generations, because we can't
00281    * free the events once they are in use
00282    */
00283   if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
00284     {
00285       dbus_cond_event_tls = TlsAlloc ();
00286       if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
00287         return FALSE;
00288     }
00289 
00290   return dbus_threads_init (&windows_functions);
00291 }
00292