• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
_dbus_win_get_dll_hmodule(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
DllMain(hinst_t hinstDLL,DWORD fdwReason,LPVOID lpvReserved)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 DBusCMutex *
_dbus_platform_cmutex_new(void)94 _dbus_platform_cmutex_new (void)
95 {
96   HANDLE handle;
97   handle = CreateMutex (NULL, FALSE, NULL);
98   return (DBusCMutex *) handle;
99 }
100 
101 DBusRMutex *
_dbus_platform_rmutex_new(void)102 _dbus_platform_rmutex_new (void)
103 {
104   HANDLE handle;
105   handle = CreateMutex (NULL, FALSE, NULL);
106   return (DBusRMutex *) handle;
107 }
108 
109 void
_dbus_platform_cmutex_free(DBusCMutex * mutex)110 _dbus_platform_cmutex_free (DBusCMutex *mutex)
111 {
112   CloseHandle ((HANDLE *) mutex);
113 }
114 
115 void
_dbus_platform_rmutex_free(DBusRMutex * mutex)116 _dbus_platform_rmutex_free (DBusRMutex *mutex)
117 {
118   CloseHandle ((HANDLE *) mutex);
119 }
120 
121 void
_dbus_platform_cmutex_lock(DBusCMutex * mutex)122 _dbus_platform_cmutex_lock (DBusCMutex *mutex)
123 {
124   WaitForSingleObject ((HANDLE *) mutex, INFINITE);
125 }
126 
127 void
_dbus_platform_rmutex_lock(DBusRMutex * mutex)128 _dbus_platform_rmutex_lock (DBusRMutex *mutex)
129 {
130   WaitForSingleObject ((HANDLE *) mutex, INFINITE);
131 }
132 
133 void
_dbus_platform_cmutex_unlock(DBusCMutex * mutex)134 _dbus_platform_cmutex_unlock (DBusCMutex *mutex)
135 {
136   ReleaseMutex ((HANDLE *) mutex);
137 }
138 
139 void
_dbus_platform_rmutex_unlock(DBusRMutex * mutex)140 _dbus_platform_rmutex_unlock (DBusRMutex *mutex)
141 {
142   ReleaseMutex ((HANDLE *) mutex);
143 }
144 
145 DBusCondVar *
_dbus_platform_condvar_new(void)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 
160 void
_dbus_platform_condvar_free(DBusCondVar * cond)161 _dbus_platform_condvar_free (DBusCondVar *cond)
162 {
163   DeleteCriticalSection (&cond->lock);
164   _dbus_list_clear (&cond->list);
165   dbus_free (cond);
166 }
167 
168 static dbus_bool_t
_dbus_condvar_wait_win32(DBusCondVar * cond,DBusCMutex * mutex,int milliseconds)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 
227 void
_dbus_platform_condvar_wait(DBusCondVar * cond,DBusCMutex * mutex)228 _dbus_platform_condvar_wait (DBusCondVar *cond,
229                              DBusCMutex  *mutex)
230 {
231   _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
232 }
233 
234 dbus_bool_t
_dbus_platform_condvar_wait_timeout(DBusCondVar * cond,DBusCMutex * mutex,int timeout_milliseconds)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 
242 void
_dbus_platform_condvar_wake_one(DBusCondVar * cond)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 
259 dbus_bool_t
_dbus_threads_init_platform_specific(void)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