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