dbus-sysdeps-thread-win.c 7.18 KB
Newer Older
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* dbus-sysdeps-pthread.c Implements threads using Windows threads (internal to libdbus)
 * 
 * Copyright (C) 2006  Red Hat, Inc.
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * 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 26
#include "dbus-internals.h"
#include "dbus-sysdeps.h"
27
#include "dbus-sysdeps-win.h"
28
#include "dbus-threads.h"
29
#include "dbus-list.h"
30

31 32
#include <stdio.h>

33 34
#include <windows.h>

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
static dbus_bool_t global_init_done = FALSE;
static CRITICAL_SECTION init_lock;

/* Called from C++ code in dbus-init-win.cpp. */
void
_dbus_threads_windows_init_global (void)
{
  /* this ensures that the object that acts as our global constructor
   * actually gets linked in when we're linked statically */
  _dbus_threads_windows_ensure_ctor_linked ();

  InitializeCriticalSection (&init_lock);
  global_init_done = TRUE;
}

50 51 52 53 54 55 56 57
struct DBusCondVar {
  DBusList *list;        /**< list thread-local-stored events waiting on the cond variable */
  CRITICAL_SECTION lock; /**< lock protecting the list */
};

static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES;


58 59 60 61 62 63 64 65
static HMODULE dbus_dll_hmodule;

void *
_dbus_win_get_dll_hmodule (void)
{
  return dbus_dll_hmodule;
}

66 67 68 69 70 71 72
#ifdef DBUS_WINCE
#define hinst_t HANDLE
#else
#define hinst_t HINSTANCE
#endif

BOOL WINAPI DllMain (hinst_t, DWORD, LPVOID);
73 74 75

/* We need this to free the TLS events on thread exit */
BOOL WINAPI
76
DllMain (hinst_t hinstDLL,
77 78 79 80 81 82
	 DWORD     fdwReason,
	 LPVOID    lpvReserved)
{
  HANDLE event;
  switch (fdwReason) 
    { 
83 84 85
    case DLL_PROCESS_ATTACH:
      dbus_dll_hmodule = hinstDLL;
      break;
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    case DLL_THREAD_DETACH:
      if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
	{
	  event = TlsGetValue(dbus_cond_event_tls);
	  CloseHandle (event);
	  TlsSetValue(dbus_cond_event_tls, NULL);
	}
      break;
    case DLL_PROCESS_DETACH: 
      if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES)
	{
	  event = TlsGetValue(dbus_cond_event_tls);
	  CloseHandle (event);
	  TlsSetValue(dbus_cond_event_tls, NULL);

	  TlsFree(dbus_cond_event_tls); 
	}
      break;
    default: 
      break; 
    }
  return TRUE;
}

110 111
DBusCMutex *
_dbus_platform_cmutex_new (void)
112 113 114
{
  HANDLE handle;
  handle = CreateMutex (NULL, FALSE, NULL);
115
  return (DBusCMutex *) handle;
116 117
}

118 119 120 121 122 123 124 125 126 127
DBusRMutex *
_dbus_platform_rmutex_new (void)
{
  HANDLE handle;
  handle = CreateMutex (NULL, FALSE, NULL);
  return (DBusRMutex *) handle;
}

void
_dbus_platform_cmutex_free (DBusCMutex *mutex)
128 129 130 131
{
  CloseHandle ((HANDLE *) mutex);
}

132 133
void
_dbus_platform_rmutex_free (DBusRMutex *mutex)
134
{
135
  CloseHandle ((HANDLE *) mutex);
136 137
}

138 139 140 141 142 143 144 145
void
_dbus_platform_cmutex_lock (DBusCMutex *mutex)
{
  WaitForSingleObject ((HANDLE *) mutex, INFINITE);
}

void
_dbus_platform_rmutex_lock (DBusRMutex *mutex)
146
{
147
  WaitForSingleObject ((HANDLE *) mutex, INFINITE);
148 149
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163
void
_dbus_platform_cmutex_unlock (DBusCMutex *mutex)
{
  ReleaseMutex ((HANDLE *) mutex);
}

void
_dbus_platform_rmutex_unlock (DBusRMutex *mutex)
{
  ReleaseMutex ((HANDLE *) mutex);
}

DBusCondVar *
_dbus_platform_condvar_new (void)
164 165 166 167 168 169 170 171 172 173
{
  DBusCondVar *cond;
    
  cond = dbus_new (DBusCondVar, 1);
  if (cond == NULL)
    return NULL;
  
  cond->list = NULL;
  
  InitializeCriticalSection (&cond->lock);
174
  return cond;
175 176
}

177 178
void
_dbus_platform_condvar_free (DBusCondVar *cond)
179 180 181 182 183 184 185 186
{
  DeleteCriticalSection (&cond->lock);
  _dbus_list_clear (&cond->list);
  dbus_free (cond);
}

static dbus_bool_t
_dbus_condvar_wait_win32 (DBusCondVar *cond,
187
			  DBusCMutex *mutex,
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
			  int milliseconds)
{
  DWORD retval;
  dbus_bool_t ret;
  HANDLE event = TlsGetValue (dbus_cond_event_tls);

  if (!event)
    {
      event = CreateEvent (0, FALSE, FALSE, NULL);
      if (event == 0)
	return FALSE;
      TlsSetValue (dbus_cond_event_tls, event);
    }

  EnterCriticalSection (&cond->lock);

  /* The event must not be signaled. Check this */
  _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);

  ret = _dbus_list_append (&cond->list, event);
  
  LeaveCriticalSection (&cond->lock);
  
  if (!ret)
    return FALSE; /* Prepend failed */

214
  _dbus_platform_cmutex_unlock (mutex);
215
  retval = WaitForSingleObject (event, milliseconds);
216
  _dbus_platform_cmutex_lock (mutex);
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
  
  if (retval == WAIT_TIMEOUT)
    {
      EnterCriticalSection (&cond->lock);
      _dbus_list_remove (&cond->list, event);

      /* In the meantime we could have been signaled, so we must again
       * wait for the signal, this time with no timeout, to reset
       * it. retval is set again to honour the late arrival of the
       * signal */
      retval = WaitForSingleObject (event, 0);

      LeaveCriticalSection (&cond->lock);
    }

#ifndef DBUS_DISABLE_ASSERT
  EnterCriticalSection (&cond->lock);

  /* Now event must not be inside the array, check this */
236
  _dbus_assert (_dbus_list_remove (&cond->list, event) == FALSE);
237 238 239 240 241 242 243

  LeaveCriticalSection (&cond->lock);
#endif /* !G_DISABLE_ASSERT */

  return retval != WAIT_TIMEOUT;
}

244 245 246
void
_dbus_platform_condvar_wait (DBusCondVar *cond,
                             DBusCMutex  *mutex)
247 248 249 250
{
  _dbus_condvar_wait_win32 (cond, mutex, INFINITE);
}

251 252 253
dbus_bool_t
_dbus_platform_condvar_wait_timeout (DBusCondVar               *cond,
				     DBusCMutex                *mutex,
254 255 256 257 258
				     int                        timeout_milliseconds)
{
  return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds);
}

259 260
void
_dbus_platform_condvar_wake_one (DBusCondVar *cond)
261 262 263 264
{
  EnterCriticalSection (&cond->lock);
  
  if (cond->list != NULL)
265 266 267 268 269 270 271 272
    {
      SetEvent (_dbus_list_pop_first (&cond->list));
      /* Avoid live lock by pushing the waiter to the mutex lock
         instruction, which is fair.  If we don't do this, we could
         acquire the condition variable again before the waiter has a
         chance itself, leading to starvation.  */
      Sleep (0);
    }
273 274 275
  LeaveCriticalSection (&cond->lock);
}

276
dbus_bool_t
277 278 279 280 281 282 283 284 285 286 287 288
_dbus_threads_init_platform_specific (void)
{
  /* We reuse this over several generations, because we can't
   * free the events once they are in use
   */
  if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
    {
      dbus_cond_event_tls = TlsAlloc ();
      if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES)
	return FALSE;
    }

289
  return TRUE;
290 291
}

292 293 294 295 296 297 298 299 300 301 302 303 304
void
_dbus_threads_lock_platform_specific (void)
{
  _dbus_assert (global_init_done);
  EnterCriticalSection (&init_lock);
}

void
_dbus_threads_unlock_platform_specific (void)
{
  _dbus_assert (global_init_done);
  LeaveCriticalSection (&init_lock);
}
305 306 307 308 309 310 311 312

#ifdef DBUS_ENABLE_VERBOSE_MODE
void
_dbus_print_thread (void)
{
  fprintf (stderr, "%lu: 0x%04lx: ", _dbus_pid_for_log (), GetCurrentThreadId ());
}
#endif