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