1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2/* dbus-sysdeps-pthread.c Implements threads using pthreads (internal to libdbus) 3 * 4 * Copyright (C) 2002, 2003, 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-threads.h" 28 29#include <sys/time.h> 30#include <pthread.h> 31#include <string.h> 32 33#ifdef HAVE_ERRNO_H 34#include <errno.h> 35#endif 36 37#include <config.h> 38 39/* Whether we have a "monotonic" clock; i.e. a clock not affected by 40 * changes in system time. 41 * This is initialized once in check_monotonic_clock below. 42 * https://bugs.freedesktop.org/show_bug.cgi?id=18121 43 */ 44static dbus_bool_t have_monotonic_clock = 0; 45 46typedef struct { 47 pthread_mutex_t lock; /**< lock protecting count field */ 48 volatile int count; /**< count of how many times lock holder has recursively locked */ 49 volatile pthread_t holder; /**< holder of the lock if count >0, 50 valid but arbitrary thread if count 51 has ever been >0, uninitialized memory 52 if count has never been >0 */ 53} DBusMutexPThread; 54 55typedef struct { 56 pthread_cond_t cond; /**< the condition */ 57} DBusCondVarPThread; 58 59#define DBUS_MUTEX(m) ((DBusMutex*) m) 60#define DBUS_MUTEX_PTHREAD(m) ((DBusMutexPThread*) m) 61 62#define DBUS_COND_VAR(c) ((DBusCondVar*) c) 63#define DBUS_COND_VAR_PTHREAD(c) ((DBusCondVarPThread*) c) 64 65 66#ifdef DBUS_DISABLE_ASSERT 67/* (tmp != 0) is a no-op usage to silence compiler */ 68#define PTHREAD_CHECK(func_name, result_or_call) \ 69 do { int tmp = (result_or_call); if (tmp != 0) {;} } while (0) 70#else 71#define PTHREAD_CHECK(func_name, result_or_call) do { \ 72 int tmp = (result_or_call); \ 73 if (tmp != 0) { \ 74 _dbus_warn_check_failed ("pthread function %s failed with %d %s in %s\n", \ 75 func_name, tmp, strerror(tmp), _DBUS_FUNCTION_NAME); \ 76 } \ 77} while (0) 78#endif /* !DBUS_DISABLE_ASSERT */ 79 80static DBusMutex* 81_dbus_pthread_mutex_new (void) 82{ 83 DBusMutexPThread *pmutex; 84 int result; 85 86 pmutex = dbus_new (DBusMutexPThread, 1); 87 if (pmutex == NULL) 88 return NULL; 89 90 result = pthread_mutex_init (&pmutex->lock, NULL); 91 92 if (result == ENOMEM || result == EAGAIN) 93 { 94 dbus_free (pmutex); 95 return NULL; 96 } 97 else 98 { 99 PTHREAD_CHECK ("pthread_mutex_init", result); 100 } 101 102 /* Only written */ 103 pmutex->count = 0; 104 105 /* There's no portable way to have a "null" pthread afaik so we 106 * can't set pmutex->holder to anything sensible. We only access it 107 * once the lock is held (which means we've set it). 108 */ 109 110 return DBUS_MUTEX (pmutex); 111} 112 113static void 114_dbus_pthread_mutex_free (DBusMutex *mutex) 115{ 116 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 117 118 _dbus_assert (pmutex->count == 0); 119 120 PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&pmutex->lock)); 121 122 dbus_free (pmutex); 123} 124 125static void 126_dbus_pthread_mutex_lock (DBusMutex *mutex) 127{ 128 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 129 pthread_t self = pthread_self (); 130 131 /* If the count is > 0 then someone had the lock, maybe us. If it is 132 * 0, then it might immediately change right after we read it, 133 * but it will be changed by another thread; i.e. if we read 0, 134 * we assume that this thread doesn't have the lock. 135 * 136 * Not 100% sure this is safe, but ... seems like it should be. 137 */ 138 if (pmutex->count == 0) 139 { 140 /* We know we don't have the lock; someone may have the lock. */ 141 142 PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); 143 144 /* We now have the lock. Count must be 0 since it must be 0 when 145 * the lock is released by another thread, and we just now got 146 * the lock. 147 */ 148 _dbus_assert (pmutex->count == 0); 149 150 pmutex->holder = self; 151 pmutex->count = 1; 152 } 153 else 154 { 155 /* We know someone had the lock, possibly us. Thus 156 * pmutex->holder is not pointing to junk, though it may not be 157 * the lock holder anymore if the lock holder is not us. If the 158 * lock holder is us, then we definitely have the lock. 159 */ 160 161 if (pthread_equal (pmutex->holder, self)) 162 { 163 /* We already have the lock. */ 164 _dbus_assert (pmutex->count > 0); 165 } 166 else 167 { 168 /* Wait for the lock */ 169 PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); 170 pmutex->holder = self; 171 _dbus_assert (pmutex->count == 0); 172 } 173 174 pmutex->count += 1; 175 } 176} 177 178static void 179_dbus_pthread_mutex_unlock (DBusMutex *mutex) 180{ 181 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 182 183 _dbus_assert (pmutex->count > 0); 184 185 pmutex->count -= 1; 186 187 if (pmutex->count == 0) 188 PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&pmutex->lock)); 189 190 /* We leave pmutex->holder set to ourselves, its content is undefined if count is 0 */ 191} 192 193static DBusCondVar * 194_dbus_pthread_condvar_new (void) 195{ 196 DBusCondVarPThread *pcond; 197 pthread_condattr_t attr; 198 int result; 199 200 pcond = dbus_new (DBusCondVarPThread, 1); 201 if (pcond == NULL) 202 return NULL; 203 204 pthread_condattr_init (&attr); 205#ifdef HAVE_MONOTONIC_CLOCK 206 if (have_monotonic_clock) 207 pthread_condattr_setclock (&attr, CLOCK_MONOTONIC); 208#endif 209 210 result = pthread_cond_init (&pcond->cond, &attr); 211 pthread_condattr_destroy (&attr); 212 213 if (result == EAGAIN || result == ENOMEM) 214 { 215 dbus_free (pcond); 216 return NULL; 217 } 218 else 219 { 220 PTHREAD_CHECK ("pthread_cond_init", result); 221 } 222 223 return DBUS_COND_VAR (pcond); 224} 225 226static void 227_dbus_pthread_condvar_free (DBusCondVar *cond) 228{ 229 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 230 231 PTHREAD_CHECK ("pthread_cond_destroy", pthread_cond_destroy (&pcond->cond)); 232 233 dbus_free (pcond); 234} 235 236static void 237_dbus_pthread_condvar_wait (DBusCondVar *cond, 238 DBusMutex *mutex) 239{ 240 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 241 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 242 int old_count; 243 244 _dbus_assert (pmutex->count > 0); 245 _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); 246 247 old_count = pmutex->count; 248 pmutex->count = 0; /* allow other threads to lock */ 249 PTHREAD_CHECK ("pthread_cond_wait", pthread_cond_wait (&pcond->cond, &pmutex->lock)); 250 _dbus_assert (pmutex->count == 0); 251 pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ 252 253 /* The order of this line and the above line is important. 254 * See the comments below at the end of _dbus_pthread_condvar_wait_timeout 255 */ 256 pmutex->count = old_count; 257} 258 259static dbus_bool_t 260_dbus_pthread_condvar_wait_timeout (DBusCondVar *cond, 261 DBusMutex *mutex, 262 int timeout_milliseconds) 263{ 264 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 265 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 266 struct timeval time_now; 267 struct timespec end_time; 268 int result; 269 int old_count; 270 271 _dbus_assert (pmutex->count > 0); 272 _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); 273 274#ifdef HAVE_MONOTONIC_CLOCK 275 if (have_monotonic_clock) 276 { 277 struct timespec monotonic_timer; 278 clock_gettime (CLOCK_MONOTONIC,&monotonic_timer); 279 time_now.tv_sec = monotonic_timer.tv_sec; 280 time_now.tv_usec = monotonic_timer.tv_nsec / 1000; 281 } 282 else 283 /* This else falls through to gettimeofday */ 284#endif 285 gettimeofday (&time_now, NULL); 286 287 end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000; 288 end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000; 289 if (end_time.tv_nsec > 1000*1000*1000) 290 { 291 end_time.tv_sec += 1; 292 end_time.tv_nsec -= 1000*1000*1000; 293 } 294 295 old_count = pmutex->count; 296 pmutex->count = 0; 297 result = pthread_cond_timedwait (&pcond->cond, &pmutex->lock, &end_time); 298 299 if (result != ETIMEDOUT) 300 { 301 PTHREAD_CHECK ("pthread_cond_timedwait", result); 302 } 303 304 _dbus_assert (pmutex->count == 0); 305 pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ 306 307 /* restore to old count after setting the owner back to self, 308 * If reversing this line with above line, the previous owner thread could 309 * get into the mutex without proper locking by passing the lock owner check. 310 */ 311 pmutex->count = old_count; 312 313 /* return true if we did not time out */ 314 return result != ETIMEDOUT; 315} 316 317static void 318_dbus_pthread_condvar_wake_one (DBusCondVar *cond) 319{ 320 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 321 322 PTHREAD_CHECK ("pthread_cond_signal", pthread_cond_signal (&pcond->cond)); 323} 324 325static void 326_dbus_pthread_condvar_wake_all (DBusCondVar *cond) 327{ 328 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 329 330 PTHREAD_CHECK ("pthread_cond_broadcast", pthread_cond_broadcast (&pcond->cond)); 331} 332 333static const DBusThreadFunctions pthread_functions = 334{ 335 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK | 336 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK | 337 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK | 338 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK | 339 DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | 340 DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | 341 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | 342 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | 343 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| 344 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, 345 NULL, NULL, NULL, NULL, 346 _dbus_pthread_condvar_new, 347 _dbus_pthread_condvar_free, 348 _dbus_pthread_condvar_wait, 349 _dbus_pthread_condvar_wait_timeout, 350 _dbus_pthread_condvar_wake_one, 351 _dbus_pthread_condvar_wake_all, 352 _dbus_pthread_mutex_new, 353 _dbus_pthread_mutex_free, 354 _dbus_pthread_mutex_lock, 355 _dbus_pthread_mutex_unlock 356}; 357 358static void 359check_monotonic_clock (void) 360{ 361#ifdef HAVE_MONOTONIC_CLOCK 362 struct timespec dummy; 363 if (clock_getres (CLOCK_MONOTONIC, &dummy) == 0) 364 have_monotonic_clock = TRUE; 365#endif 366} 367 368dbus_bool_t 369_dbus_threads_init_platform_specific (void) 370{ 371 check_monotonic_clock (); 372 return dbus_threads_init (&pthread_functions); 373} 374