Home | History | Annotate | Download | only in dbus
      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