D-Bus 1.4.0
|
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 00002 /* dbus-sysdeps-pthread.c Implements threads using pthreads (internal to libdbus) 00003 * 00004 * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. 00005 * 00006 * Licensed under the Academic Free License version 2.1 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 * 00022 */ 00023 00024 #include <config.h> 00025 #include "dbus-internals.h" 00026 #include "dbus-sysdeps.h" 00027 #include "dbus-threads.h" 00028 00029 #include <sys/time.h> 00030 #include <pthread.h> 00031 #include <string.h> 00032 00033 #ifdef HAVE_ERRNO_H 00034 #include <errno.h> 00035 #endif 00036 00037 #include <config.h> 00038 00039 /* Whether we have a "monotonic" clock; i.e. a clock not affected by 00040 * changes in system time. 00041 * This is initialized once in check_monotonic_clock below. 00042 * https://bugs.freedesktop.org/show_bug.cgi?id=18121 00043 */ 00044 static dbus_bool_t have_monotonic_clock = 0; 00045 00046 typedef struct { 00047 pthread_mutex_t lock; 00048 volatile int count; 00049 volatile pthread_t holder; 00053 } DBusMutexPThread; 00054 00055 typedef struct { 00056 pthread_cond_t cond; 00057 } DBusCondVarPThread; 00058 00059 #define DBUS_MUTEX(m) ((DBusMutex*) m) 00060 #define DBUS_MUTEX_PTHREAD(m) ((DBusMutexPThread*) m) 00061 00062 #define DBUS_COND_VAR(c) ((DBusCondVar*) c) 00063 #define DBUS_COND_VAR_PTHREAD(c) ((DBusCondVarPThread*) c) 00064 00065 00066 #ifdef DBUS_DISABLE_ASSERT 00067 /* (tmp != 0) is a no-op usage to silence compiler */ 00068 #define PTHREAD_CHECK(func_name, result_or_call) \ 00069 do { int tmp = (result_or_call); if (tmp != 0) {;} } while (0) 00070 #else 00071 #define PTHREAD_CHECK(func_name, result_or_call) do { \ 00072 int tmp = (result_or_call); \ 00073 if (tmp != 0) { \ 00074 _dbus_warn_check_failed ("pthread function %s failed with %d %s in %s\n", \ 00075 func_name, tmp, strerror(tmp), _DBUS_FUNCTION_NAME); \ 00076 } \ 00077 } while (0) 00078 #endif /* !DBUS_DISABLE_ASSERT */ 00079 00080 static DBusMutex* 00081 _dbus_pthread_mutex_new (void) 00082 { 00083 DBusMutexPThread *pmutex; 00084 int result; 00085 00086 pmutex = dbus_new (DBusMutexPThread, 1); 00087 if (pmutex == NULL) 00088 return NULL; 00089 00090 result = pthread_mutex_init (&pmutex->lock, NULL); 00091 00092 if (result == ENOMEM || result == EAGAIN) 00093 { 00094 dbus_free (pmutex); 00095 return NULL; 00096 } 00097 else 00098 { 00099 PTHREAD_CHECK ("pthread_mutex_init", result); 00100 } 00101 00102 /* Only written */ 00103 pmutex->count = 0; 00104 00105 /* There's no portable way to have a "null" pthread afaik so we 00106 * can't set pmutex->holder to anything sensible. We only access it 00107 * once the lock is held (which means we've set it). 00108 */ 00109 00110 return DBUS_MUTEX (pmutex); 00111 } 00112 00113 static void 00114 _dbus_pthread_mutex_free (DBusMutex *mutex) 00115 { 00116 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 00117 00118 _dbus_assert (pmutex->count == 0); 00119 00120 PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&pmutex->lock)); 00121 00122 dbus_free (pmutex); 00123 } 00124 00125 static void 00126 _dbus_pthread_mutex_lock (DBusMutex *mutex) 00127 { 00128 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 00129 pthread_t self = pthread_self (); 00130 00131 /* If the count is > 0 then someone had the lock, maybe us. If it is 00132 * 0, then it might immediately change right after we read it, 00133 * but it will be changed by another thread; i.e. if we read 0, 00134 * we assume that this thread doesn't have the lock. 00135 * 00136 * Not 100% sure this is safe, but ... seems like it should be. 00137 */ 00138 if (pmutex->count == 0) 00139 { 00140 /* We know we don't have the lock; someone may have the lock. */ 00141 00142 PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); 00143 00144 /* We now have the lock. Count must be 0 since it must be 0 when 00145 * the lock is released by another thread, and we just now got 00146 * the lock. 00147 */ 00148 _dbus_assert (pmutex->count == 0); 00149 00150 pmutex->holder = self; 00151 pmutex->count = 1; 00152 } 00153 else 00154 { 00155 /* We know someone had the lock, possibly us. Thus 00156 * pmutex->holder is not pointing to junk, though it may not be 00157 * the lock holder anymore if the lock holder is not us. If the 00158 * lock holder is us, then we definitely have the lock. 00159 */ 00160 00161 if (pthread_equal (pmutex->holder, self)) 00162 { 00163 /* We already have the lock. */ 00164 _dbus_assert (pmutex->count > 0); 00165 } 00166 else 00167 { 00168 /* Wait for the lock */ 00169 PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock)); 00170 pmutex->holder = self; 00171 _dbus_assert (pmutex->count == 0); 00172 } 00173 00174 pmutex->count += 1; 00175 } 00176 } 00177 00178 static void 00179 _dbus_pthread_mutex_unlock (DBusMutex *mutex) 00180 { 00181 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 00182 00183 _dbus_assert (pmutex->count > 0); 00184 00185 pmutex->count -= 1; 00186 00187 if (pmutex->count == 0) 00188 PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&pmutex->lock)); 00189 00190 /* We leave pmutex->holder set to ourselves, its content is undefined if count is 0 */ 00191 } 00192 00193 static DBusCondVar * 00194 _dbus_pthread_condvar_new (void) 00195 { 00196 DBusCondVarPThread *pcond; 00197 pthread_condattr_t attr; 00198 int result; 00199 00200 pcond = dbus_new (DBusCondVarPThread, 1); 00201 if (pcond == NULL) 00202 return NULL; 00203 00204 pthread_condattr_init (&attr); 00205 #ifdef HAVE_MONOTONIC_CLOCK 00206 if (have_monotonic_clock) 00207 pthread_condattr_setclock (&attr, CLOCK_MONOTONIC); 00208 #endif 00209 00210 result = pthread_cond_init (&pcond->cond, &attr); 00211 pthread_condattr_destroy (&attr); 00212 00213 if (result == EAGAIN || result == ENOMEM) 00214 { 00215 dbus_free (pcond); 00216 return NULL; 00217 } 00218 else 00219 { 00220 PTHREAD_CHECK ("pthread_cond_init", result); 00221 } 00222 00223 return DBUS_COND_VAR (pcond); 00224 } 00225 00226 static void 00227 _dbus_pthread_condvar_free (DBusCondVar *cond) 00228 { 00229 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 00230 00231 PTHREAD_CHECK ("pthread_cond_destroy", pthread_cond_destroy (&pcond->cond)); 00232 00233 dbus_free (pcond); 00234 } 00235 00236 static void 00237 _dbus_pthread_condvar_wait (DBusCondVar *cond, 00238 DBusMutex *mutex) 00239 { 00240 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 00241 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 00242 int old_count; 00243 00244 _dbus_assert (pmutex->count > 0); 00245 _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); 00246 00247 old_count = pmutex->count; 00248 pmutex->count = 0; /* allow other threads to lock */ 00249 PTHREAD_CHECK ("pthread_cond_wait", pthread_cond_wait (&pcond->cond, &pmutex->lock)); 00250 _dbus_assert (pmutex->count == 0); 00251 pmutex->count = old_count; 00252 pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ 00253 } 00254 00255 static dbus_bool_t 00256 _dbus_pthread_condvar_wait_timeout (DBusCondVar *cond, 00257 DBusMutex *mutex, 00258 int timeout_milliseconds) 00259 { 00260 DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex); 00261 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 00262 struct timeval time_now; 00263 struct timespec end_time; 00264 int result; 00265 int old_count; 00266 00267 _dbus_assert (pmutex->count > 0); 00268 _dbus_assert (pthread_equal (pmutex->holder, pthread_self ())); 00269 00270 #ifdef HAVE_MONOTONIC_CLOCK 00271 if (have_monotonic_clock) 00272 { 00273 struct timespec monotonic_timer; 00274 clock_gettime (CLOCK_MONOTONIC,&monotonic_timer); 00275 time_now.tv_sec = monotonic_timer.tv_sec; 00276 time_now.tv_usec = monotonic_timer.tv_nsec / 1000; 00277 } 00278 else 00279 /* This else falls through to gettimeofday */ 00280 #endif 00281 gettimeofday (&time_now, NULL); 00282 00283 end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000; 00284 end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000; 00285 if (end_time.tv_nsec > 1000*1000*1000) 00286 { 00287 end_time.tv_sec += 1; 00288 end_time.tv_nsec -= 1000*1000*1000; 00289 } 00290 00291 old_count = pmutex->count; 00292 pmutex->count = 0; 00293 result = pthread_cond_timedwait (&pcond->cond, &pmutex->lock, &end_time); 00294 00295 if (result != ETIMEDOUT) 00296 { 00297 PTHREAD_CHECK ("pthread_cond_timedwait", result); 00298 } 00299 00300 _dbus_assert (pmutex->count == 0); 00301 pmutex->count = old_count; 00302 pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */ 00303 00304 /* return true if we did not time out */ 00305 return result != ETIMEDOUT; 00306 } 00307 00308 static void 00309 _dbus_pthread_condvar_wake_one (DBusCondVar *cond) 00310 { 00311 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 00312 00313 PTHREAD_CHECK ("pthread_cond_signal", pthread_cond_signal (&pcond->cond)); 00314 } 00315 00316 static void 00317 _dbus_pthread_condvar_wake_all (DBusCondVar *cond) 00318 { 00319 DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond); 00320 00321 PTHREAD_CHECK ("pthread_cond_broadcast", pthread_cond_broadcast (&pcond->cond)); 00322 } 00323 00324 static const DBusThreadFunctions pthread_functions = 00325 { 00326 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK | 00327 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK | 00328 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK | 00329 DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK | 00330 DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | 00331 DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | 00332 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | 00333 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | 00334 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| 00335 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, 00336 NULL, NULL, NULL, NULL, 00337 _dbus_pthread_condvar_new, 00338 _dbus_pthread_condvar_free, 00339 _dbus_pthread_condvar_wait, 00340 _dbus_pthread_condvar_wait_timeout, 00341 _dbus_pthread_condvar_wake_one, 00342 _dbus_pthread_condvar_wake_all, 00343 _dbus_pthread_mutex_new, 00344 _dbus_pthread_mutex_free, 00345 _dbus_pthread_mutex_lock, 00346 _dbus_pthread_mutex_unlock 00347 }; 00348 00349 static void 00350 check_monotonic_clock (void) 00351 { 00352 #ifdef HAVE_MONOTONIC_CLOCK 00353 struct timespec dummy; 00354 if (clock_getres (CLOCK_MONOTONIC, &dummy) == 0) 00355 have_monotonic_clock = TRUE; 00356 #endif 00357 } 00358 00359 dbus_bool_t 00360 _dbus_threads_init_platform_specific (void) 00361 { 00362 check_monotonic_clock (); 00363 return dbus_threads_init (&pthread_functions); 00364 }