1 /* -*- mode: C; c-file-style: "gnu" -*- */ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24 #include "dbus-internals.h" 25 #include "dbus-sysdeps.h" 26 #include "dbus-threads.h" 27 28 #include <sys/time.h> 29 #include <pthread.h> 30 #include <string.h> 31 32 typedef struct { 33 pthread_mutex_t lock; /**< lock protecting count field */ 34 volatile int count; /**< count of how many times lock holder has recursively locked */ 35 volatile pthread_t holder; /**< holder of the lock if count >0, 36 valid but arbitrary thread if count 37 has ever been >0, uninitialized memory 38 if count has never been >0 */ 39 } DBusMutexPThread; 40 41 typedef struct { 42 pthread_cond_t cond; /**< the condition */ 43 } DBusCondVarPThread; 44 45 #define DBUS_MUTEX(m) ((DBusMutex*) m) 46 #define DBUS_MUTEX_PTHREAD(m) ((DBusMutexPThread*) m) 47 48 #define DBUS_COND_VAR(c) ((DBusCondVar*) c) 49 #define DBUS_COND_VAR_PTHREAD(c) ((DBusCondVarPThread*) c) 50 51 52 #ifdef DBUS_DISABLE_ASSERT 53 /* (tmp != 0) is a no-op usage to silence compiler */ 54 #define PTHREAD_CHECK(func_name, result_or_call) \ 55 do { int tmp = (result_or_call); if (tmp != 0) {;} } while (0) 56 #else 57 #define PTHREAD_CHECK(func_name, result_or_call) do { \ 58 int tmp = (result_or_call); \ 59 if (tmp != 0) { \ 60 _dbus_warn_check_failed ("pthread function %s failed with %d %s in %s\n", \ 61 func_name, tmp, strerror(tmp), _DBUS_FUNCTION_NAME); \ 62 } \ 63 } while (0) 64 #endif /* !DBUS_DISABLE_ASSERT */ 65 66 static DBusMutex* 67 _dbus_pthread_mutex_new (void) 68 { 69 DBusMutexPThread *pmutex; 70 int result; 71 72 pmutex = dbus_new (DBusMutexPThread, 1); 73 if (pmutex == NULL) 74 return NULL; 75 76 result = pthread_mutex_init (&pmutex->lock, NULL); 77 78 if (result == ENOMEM || result == EAGAIN) 79 { 80 dbus_free (pmutex); 81 return NULL; 82 } 83 else 84 { 85 PTHREAD_CHECK ("pthread_mutex_init", result); 86 } 87 88 /* Only written */ 89 pmutex->count = 0; 90 91 /* There's no portable way to have a "null" pthread afaik so we 92 * can't set pmutex->holder to anything sensible. We only access it 93 * once the lock is held (which means we've set it). 94 */ 95 96 return DBUS_MUTEX (pmutex); 97 } 98 99 static void 100 _dbus_pthread_mutex_free (DBusMutex *mutex) 101 { 102 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 103 104 _dbus_assert (pmutex->count == 0); 105 106 PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&pmutex->lock)); 107 108 dbus_free (pmutex); 109 } 110 111 static void 112 _dbus_pthread_mutex_lock (DBusMutex *mutex) 113 { 114 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 115 pthread_t self = pthread_self (); 116 117 /* If the count is > 0 then someone had the lock, maybe us. If it is 118 * 0, then it might immediately change right after we read it, 119 * but it will be changed by another thread; i.e. if we read 0, 120 * we assume that this thread doesn't have the lock. 121 * 122 * Not 100% sure this is safe, but ... seems like it should be. 123 */ 124 if (pmutex->count == 0) 125 { 126 /* We know we don't have the lock; someone may have the lock. */ 127 128 PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); 129 130 /* We now have the lock. Count must be 0 since it must be 0 when 131 * the lock is released by another thread, and we just now got 132 * the lock. 133 */ 134 _dbus_assert (pmutex->count == 0); 135 136 pmutex->holder = self; 137 pmutex->count = 1; 138 } 139 else 140 { 141 /* We know someone had the lock, possibly us. Thus 142 * pmutex->holder is not pointing to junk, though it may not be 143 * the lock holder anymore if the lock holder is not us. If the 144 * lock holder is us, then we definitely have the lock. 145 */ 146 147 if (pthread_equal (pmutex->holder, self)) 148 { 149 /* We already have the lock. */ 150 _dbus_assert (pmutex->count > 0); 151 } 152 else 153 { 154 /* Wait for the lock */ 155 PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); 156 pmutex->holder = self; 157 _dbus_assert (pmutex->count == 0); 158 } 159 160 pmutex->count += 1; 161 } 162 } 163 164 static void 165 _dbus_pthread_mutex_unlock (DBusMutex *mutex) 166 { 167 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 168 169 _dbus_assert (pmutex->count > 0); 170 171 pmutex->count -= 1; 172 173 if (pmutex->count == 0) 174 PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&pmutex->lock)); 175 176 /* We leave pmutex->holder set to ourselves, its content is undefined if count is 0 */ 177 } 178 179 static DBusCondVar * 180 _dbus_pthread_condvar_new (void) 181 { 182 DBusCondVarPThread *pcond; 183 int result; 184 185 pcond = dbus_new (DBusCondVarPThread, 1); 186 if (pcond == NULL) 187 return NULL; 188 189 result = pthread_cond_init (&pcond->cond, NULL); 190 191 if (result == EAGAIN || result == ENOMEM) 192 { 193 dbus_free (pcond); 194 return NULL; 195 } 196 else 197 { 198 PTHREAD_CHECK ("pthread_cond_init", result); 199 } 200 201 return DBUS_COND_VAR (pcond); 202 } 203 204 static void 205 _dbus_pthread_condvar_free (DBusCondVar *cond) 206 { 207 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 208 209 PTHREAD_CHECK ("pthread_cond_destroy", pthread_cond_destroy (&pcond->cond)); 210 211 dbus_free (pcond); 212 } 213 214 static void 215 _dbus_pthread_condvar_wait (DBusCondVar *cond, 216 DBusMutex *mutex) 217 { 218 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 219 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 220 int old_count; 221 222 _dbus_assert (pmutex->count > 0); 223 _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); 224 225 old_count = pmutex->count; 226 pmutex->count = 0; /* allow other threads to lock */ 227 PTHREAD_CHECK ("pthread_cond_wait", pthread_cond_wait (&pcond->cond, &pmutex->lock)); 228 _dbus_assert (pmutex->count == 0); 229 pmutex->count = old_count; 230 pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ 231 } 232 233 static dbus_bool_t 234 _dbus_pthread_condvar_wait_timeout (DBusCondVar *cond, 235 DBusMutex *mutex, 236 int timeout_milliseconds) 237 { 238 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 239 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 240 struct timeval time_now; 241 struct timespec end_time; 242 int result; 243 int old_count; 244 245 _dbus_assert (pmutex->count > 0); 246 _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); 247 248 gettimeofday (&time_now, NULL); 249 250 end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000; 251 end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000; 252 if (end_time.tv_nsec > 1000*1000*1000) 253 { 254 end_time.tv_sec += 1; 255 end_time.tv_nsec -= 1000*1000*1000; 256 } 257 258 old_count = pmutex->count; 259 pmutex->count = 0; 260 result = pthread_cond_timedwait (&pcond->cond, &pmutex->lock, &end_time); 261 262 if (result != ETIMEDOUT) 263 { 264 PTHREAD_CHECK ("pthread_cond_timedwait", result); 265 } 266 267 _dbus_assert (pmutex->count == 0); 268 pmutex->count = old_count; 269 pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ 270 271 /* return true if we did not time out */ 272 return result != ETIMEDOUT; 273 } 274 275 static void 276 _dbus_pthread_condvar_wake_one (DBusCondVar *cond) 277 { 278 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 279 280 PTHREAD_CHECK ("pthread_cond_signal", pthread_cond_signal (&pcond->cond)); 281 } 282 283 static void 284 _dbus_pthread_condvar_wake_all (DBusCondVar *cond) 285 { 286 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 287 288 PTHREAD_CHECK ("pthread_cond_broadcast", pthread_cond_broadcast (&pcond->cond)); 289 } 290 291 static const DBusThreadFunctions pthread_functions = 292 { 293 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK | 294 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK | 295 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK | 296 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK | 297 DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | 298 DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | 299 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | 300 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | 301 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| 302 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, 303 NULL, NULL, NULL, NULL, 304 _dbus_pthread_condvar_new, 305 _dbus_pthread_condvar_free, 306 _dbus_pthread_condvar_wait, 307 _dbus_pthread_condvar_wait_timeout, 308 _dbus_pthread_condvar_wake_one, 309 _dbus_pthread_condvar_wake_all, 310 _dbus_pthread_mutex_new, 311 _dbus_pthread_mutex_free, 312 _dbus_pthread_mutex_lock, 313 _dbus_pthread_mutex_unlock 314 }; 315 316 dbus_bool_t 317 _dbus_threads_init_platform_specific (void) 318 { 319 return dbus_threads_init (&pthread_functions); 320 } 321