| David Reiss | ea2cba8 | 2009-03-30 21:35:00 +0000 | [diff] [blame] | 1 | /* | 
|  | 2 | * Licensed to the Apache Software Foundation (ASF) under one | 
|  | 3 | * or more contributor license agreements. See the NOTICE file | 
|  | 4 | * distributed with this work for additional information | 
|  | 5 | * regarding copyright ownership. The ASF licenses this file | 
|  | 6 | * to you under the Apache License, Version 2.0 (the | 
|  | 7 | * "License"); you may not use this file except in compliance | 
|  | 8 | * with the License. You may obtain a copy of the License at | 
|  | 9 | * | 
|  | 10 | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 11 | * | 
|  | 12 | * Unless required by applicable law or agreed to in writing, | 
|  | 13 | * software distributed under the License is distributed on an | 
|  | 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | 15 | * KIND, either express or implied. See the License for the | 
|  | 16 | * specific language governing permissions and limitations | 
|  | 17 | * under the License. | 
|  | 18 | */ | 
| Mark Slee | 9f0c651 | 2007-02-28 23:58:26 +0000 | [diff] [blame] | 19 |  | 
| Roger Meier | 2fa9c31 | 2011-09-05 19:15:53 +0000 | [diff] [blame] | 20 | #ifdef HAVE_CONFIG_H | 
|  | 21 | #include <config.h> | 
|  | 22 | #endif | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 23 | #include "Mutex.h" | 
| David Reiss | 4e19f19 | 2010-03-09 05:19:59 +0000 | [diff] [blame] | 24 | #include "Util.h" | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 25 |  | 
|  | 26 | #include <assert.h> | 
| Roger Meier | 2fa9c31 | 2011-09-05 19:15:53 +0000 | [diff] [blame] | 27 | #ifdef HAVE_PTHREAD_H | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 28 | #include <pthread.h> | 
| Roger Meier | 2fa9c31 | 2011-09-05 19:15:53 +0000 | [diff] [blame] | 29 | #endif | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 30 | #include <signal.h> | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 31 |  | 
| yunfang | 1454296 | 2007-10-03 22:59:41 +0000 | [diff] [blame] | 32 | using boost::shared_ptr; | 
|  | 33 |  | 
| T Jake Luciani | b5e6221 | 2009-01-31 22:36:20 +0000 | [diff] [blame] | 34 | namespace apache { namespace thrift { namespace concurrency { | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 35 |  | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 36 | #ifndef THRIFT_NO_CONTENTION_PROFILING | 
|  | 37 |  | 
|  | 38 | static sig_atomic_t mutexProfilingSampleRate = 0; | 
|  | 39 | static MutexWaitCallback mutexProfilingCallback = 0; | 
|  | 40 |  | 
|  | 41 | volatile static sig_atomic_t mutexProfilingCounter = 0; | 
|  | 42 |  | 
|  | 43 | void enableMutexProfiling(int32_t profilingSampleRate, | 
|  | 44 | MutexWaitCallback callback) { | 
|  | 45 | mutexProfilingSampleRate = profilingSampleRate; | 
|  | 46 | mutexProfilingCallback = callback; | 
|  | 47 | } | 
|  | 48 |  | 
|  | 49 | #define PROFILE_MUTEX_START_LOCK() \ | 
|  | 50 | int64_t _lock_startTime = maybeGetProfilingStartTime(); | 
|  | 51 |  | 
|  | 52 | #define PROFILE_MUTEX_NOT_LOCKED() \ | 
|  | 53 | do { \ | 
|  | 54 | if (_lock_startTime > 0) { \ | 
|  | 55 | int64_t endTime = Util::currentTimeUsec(); \ | 
|  | 56 | (*mutexProfilingCallback)(this, endTime - _lock_startTime); \ | 
|  | 57 | } \ | 
|  | 58 | } while (0) | 
|  | 59 |  | 
|  | 60 | #define PROFILE_MUTEX_LOCKED() \ | 
|  | 61 | do { \ | 
|  | 62 | profileTime_ = _lock_startTime; \ | 
|  | 63 | if (profileTime_ > 0) { \ | 
|  | 64 | profileTime_ = Util::currentTimeUsec() - profileTime_; \ | 
|  | 65 | } \ | 
|  | 66 | } while (0) | 
|  | 67 |  | 
|  | 68 | #define PROFILE_MUTEX_START_UNLOCK() \ | 
|  | 69 | int64_t _temp_profileTime = profileTime_; \ | 
|  | 70 | profileTime_ = 0; | 
|  | 71 |  | 
|  | 72 | #define PROFILE_MUTEX_UNLOCKED() \ | 
|  | 73 | do { \ | 
|  | 74 | if (_temp_profileTime > 0) { \ | 
|  | 75 | (*mutexProfilingCallback)(this, _temp_profileTime); \ | 
|  | 76 | } \ | 
|  | 77 | } while (0) | 
|  | 78 |  | 
|  | 79 | static inline int64_t maybeGetProfilingStartTime() { | 
|  | 80 | if (mutexProfilingSampleRate && mutexProfilingCallback) { | 
|  | 81 | // This block is unsynchronized, but should produce a reasonable sampling | 
|  | 82 | // rate on most architectures.  The main race conditions are the gap | 
|  | 83 | // between the decrement and the test, the non-atomicity of decrement, and | 
|  | 84 | // potential caching of different values at different CPUs. | 
|  | 85 | // | 
|  | 86 | // - if two decrements race, the likeliest result is that the counter | 
|  | 87 | //      decrements slowly (perhaps much more slowly) than intended. | 
|  | 88 | // | 
|  | 89 | // - many threads could potentially decrement before resetting the counter | 
|  | 90 | //      to its large value, causing each additional incoming thread to | 
|  | 91 | //      profile every call.  This situation is unlikely to persist for long | 
|  | 92 | //      as the critical gap is quite short, but profiling could be bursty. | 
|  | 93 | sig_atomic_t localValue = --mutexProfilingCounter; | 
|  | 94 | if (localValue <= 0) { | 
|  | 95 | mutexProfilingCounter = mutexProfilingSampleRate; | 
|  | 96 | return Util::currentTimeUsec(); | 
|  | 97 | } | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | return 0; | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | #else | 
|  | 104 | #  define PROFILE_MUTEX_START_LOCK() | 
|  | 105 | #  define PROFILE_MUTEX_NOT_LOCKED() | 
|  | 106 | #  define PROFILE_MUTEX_LOCKED() | 
|  | 107 | #  define PROFILE_MUTEX_START_UNLOCK() | 
|  | 108 | #  define PROFILE_MUTEX_UNLOCKED() | 
|  | 109 | #endif // THRIFT_NO_CONTENTION_PROFILING | 
|  | 110 |  | 
| David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 111 | /** | 
| Mark Slee | f5f2be4 | 2006-09-05 21:05:31 +0000 | [diff] [blame] | 112 | * Implementation of Mutex class using POSIX mutex | 
|  | 113 | * | 
| Mark Slee | f5f2be4 | 2006-09-05 21:05:31 +0000 | [diff] [blame] | 114 | * @version $Id:$ | 
|  | 115 | */ | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 116 | class Mutex::impl { | 
| Mark Slee | f5f2be4 | 2006-09-05 21:05:31 +0000 | [diff] [blame] | 117 | public: | 
| David Reiss | c6dab61 | 2008-06-10 22:55:13 +0000 | [diff] [blame] | 118 | impl(Initializer init) : initialized_(false) { | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 119 | #ifndef THRIFT_NO_CONTENTION_PROFILING | 
|  | 120 | profileTime_ = 0; | 
|  | 121 | #endif | 
| David Reiss | c6dab61 | 2008-06-10 22:55:13 +0000 | [diff] [blame] | 122 | init(&pthread_mutex_); | 
| Mark Slee | 2f6404d | 2006-10-10 01:37:40 +0000 | [diff] [blame] | 123 | initialized_ = true; | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 124 | } | 
|  | 125 |  | 
|  | 126 | ~impl() { | 
| Mark Slee | 2f6404d | 2006-10-10 01:37:40 +0000 | [diff] [blame] | 127 | if (initialized_) { | 
|  | 128 | initialized_ = false; | 
| Aditya Agarwal | 9dc5740 | 2007-03-31 17:45:12 +0000 | [diff] [blame] | 129 | int ret = pthread_mutex_destroy(&pthread_mutex_); | 
| Aditya Agarwal | 3f234da | 2007-04-01 01:19:57 +0000 | [diff] [blame] | 130 | assert(ret == 0); | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 131 | } | 
|  | 132 | } | 
|  | 133 |  | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 134 | void lock() const { | 
|  | 135 | PROFILE_MUTEX_START_LOCK(); | 
|  | 136 | pthread_mutex_lock(&pthread_mutex_); | 
|  | 137 | PROFILE_MUTEX_LOCKED(); | 
|  | 138 | } | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 139 |  | 
| boz | 5362e70 | 2007-08-15 20:55:36 +0000 | [diff] [blame] | 140 | bool trylock() const { return (0 == pthread_mutex_trylock(&pthread_mutex_)); } | 
|  | 141 |  | 
| David Reiss | 4e19f19 | 2010-03-09 05:19:59 +0000 | [diff] [blame] | 142 | bool timedlock(int64_t milliseconds) const { | 
| David Reiss | 318a328 | 2010-03-22 02:34:57 +0000 | [diff] [blame] | 143 | #if defined(_POSIX_TIMEOUTS) && _POSIX_TIMEOUTS >= 200112L | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 144 | PROFILE_MUTEX_START_LOCK(); | 
|  | 145 |  | 
| David Reiss | 4e19f19 | 2010-03-09 05:19:59 +0000 | [diff] [blame] | 146 | struct timespec ts; | 
|  | 147 | Util::toTimespec(ts, milliseconds); | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 148 | int ret = pthread_mutex_timedlock(&pthread_mutex_, &ts); | 
|  | 149 | if (ret == 0) { | 
|  | 150 | PROFILE_MUTEX_LOCKED(); | 
|  | 151 | return true; | 
|  | 152 | } | 
|  | 153 |  | 
|  | 154 | PROFILE_MUTEX_NOT_LOCKED(); | 
|  | 155 | return false; | 
| David Reiss | 318a328 | 2010-03-22 02:34:57 +0000 | [diff] [blame] | 156 | #else | 
|  | 157 | // If pthread_mutex_timedlock isn't supported, the safest thing to do | 
|  | 158 | // is just do a nonblocking trylock. | 
|  | 159 | return trylock(); | 
|  | 160 | #endif | 
| David Reiss | 4e19f19 | 2010-03-09 05:19:59 +0000 | [diff] [blame] | 161 | } | 
|  | 162 |  | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 163 | void unlock() const { | 
|  | 164 | PROFILE_MUTEX_START_UNLOCK(); | 
|  | 165 | pthread_mutex_unlock(&pthread_mutex_); | 
|  | 166 | PROFILE_MUTEX_UNLOCKED(); | 
|  | 167 | } | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 168 |  | 
| David Reiss | b9db49c | 2010-03-09 05:19:30 +0000 | [diff] [blame] | 169 | void* getUnderlyingImpl() const { return (void*) &pthread_mutex_; } | 
|  | 170 |  | 
| Mark Slee | f5f2be4 | 2006-09-05 21:05:31 +0000 | [diff] [blame] | 171 | private: | 
| Mark Slee | 2f6404d | 2006-10-10 01:37:40 +0000 | [diff] [blame] | 172 | mutable pthread_mutex_t pthread_mutex_; | 
|  | 173 | mutable bool initialized_; | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 174 | #ifndef THRIFT_NO_CONTENTION_PROFILING | 
|  | 175 | mutable int64_t profileTime_; | 
|  | 176 | #endif | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 177 | }; | 
|  | 178 |  | 
| David Reiss | c6dab61 | 2008-06-10 22:55:13 +0000 | [diff] [blame] | 179 | Mutex::Mutex(Initializer init) : impl_(new Mutex::impl(init)) {} | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 180 |  | 
| David Reiss | b9db49c | 2010-03-09 05:19:30 +0000 | [diff] [blame] | 181 | void* Mutex::getUnderlyingImpl() const { return impl_->getUnderlyingImpl(); } | 
|  | 182 |  | 
| Mark Slee | 2f6404d | 2006-10-10 01:37:40 +0000 | [diff] [blame] | 183 | void Mutex::lock() const { impl_->lock(); } | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 184 |  | 
| boz | 5362e70 | 2007-08-15 20:55:36 +0000 | [diff] [blame] | 185 | bool Mutex::trylock() const { return impl_->trylock(); } | 
|  | 186 |  | 
| David Reiss | 4e19f19 | 2010-03-09 05:19:59 +0000 | [diff] [blame] | 187 | bool Mutex::timedlock(int64_t ms) const { return impl_->timedlock(ms); } | 
|  | 188 |  | 
| Mark Slee | 2f6404d | 2006-10-10 01:37:40 +0000 | [diff] [blame] | 189 | void Mutex::unlock() const { impl_->unlock(); } | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 190 |  | 
| David Reiss | c6dab61 | 2008-06-10 22:55:13 +0000 | [diff] [blame] | 191 | void Mutex::DEFAULT_INITIALIZER(void* arg) { | 
|  | 192 | pthread_mutex_t* pthread_mutex = (pthread_mutex_t*)arg; | 
|  | 193 | int ret = pthread_mutex_init(pthread_mutex, NULL); | 
|  | 194 | assert(ret == 0); | 
|  | 195 | } | 
|  | 196 |  | 
| Roger Meier | 178f8f2 | 2010-10-25 12:36:04 +0000 | [diff] [blame] | 197 | #if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) || defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) | 
| David Reiss | c6dab61 | 2008-06-10 22:55:13 +0000 | [diff] [blame] | 198 | static void init_with_kind(pthread_mutex_t* mutex, int kind) { | 
|  | 199 | pthread_mutexattr_t mutexattr; | 
|  | 200 | int ret = pthread_mutexattr_init(&mutexattr); | 
|  | 201 | assert(ret == 0); | 
|  | 202 |  | 
|  | 203 | // Apparently, this can fail.  Should we really be aborting? | 
|  | 204 | ret = pthread_mutexattr_settype(&mutexattr, kind); | 
|  | 205 | assert(ret == 0); | 
|  | 206 |  | 
|  | 207 | ret = pthread_mutex_init(mutex, &mutexattr); | 
|  | 208 | assert(ret == 0); | 
|  | 209 |  | 
|  | 210 | ret = pthread_mutexattr_destroy(&mutexattr); | 
|  | 211 | assert(ret == 0); | 
|  | 212 | } | 
| Roger Meier | 178f8f2 | 2010-10-25 12:36:04 +0000 | [diff] [blame] | 213 | #endif | 
| David Reiss | c6dab61 | 2008-06-10 22:55:13 +0000 | [diff] [blame] | 214 |  | 
|  | 215 | #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP | 
|  | 216 | void Mutex::ADAPTIVE_INITIALIZER(void* arg) { | 
|  | 217 | // From mysql source: mysys/my_thr_init.c | 
|  | 218 | // Set mutex type to "fast" a.k.a "adaptive" | 
|  | 219 | // | 
|  | 220 | // In this case the thread may steal the mutex from some other thread | 
|  | 221 | // that is waiting for the same mutex. This will save us some | 
|  | 222 | // context switches but may cause a thread to 'starve forever' while | 
|  | 223 | // waiting for the mutex (not likely if the code within the mutex is | 
|  | 224 | // short). | 
|  | 225 | init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_ADAPTIVE_NP); | 
|  | 226 | } | 
|  | 227 | #endif | 
|  | 228 |  | 
|  | 229 | #ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP | 
|  | 230 | void Mutex::RECURSIVE_INITIALIZER(void* arg) { | 
|  | 231 | init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_RECURSIVE_NP); | 
|  | 232 | } | 
|  | 233 | #endif | 
|  | 234 |  | 
|  | 235 |  | 
| David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 236 | /** | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 237 | * Implementation of ReadWriteMutex class using POSIX rw lock | 
|  | 238 | * | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 239 | * @version $Id:$ | 
|  | 240 | */ | 
|  | 241 | class ReadWriteMutex::impl { | 
|  | 242 | public: | 
|  | 243 | impl() : initialized_(false) { | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 244 | #ifndef THRIFT_NO_CONTENTION_PROFILING | 
|  | 245 | profileTime_ = 0; | 
|  | 246 | #endif | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 247 | int ret = pthread_rwlock_init(&rw_lock_, NULL); | 
|  | 248 | assert(ret == 0); | 
|  | 249 | initialized_ = true; | 
|  | 250 | } | 
|  | 251 |  | 
|  | 252 | ~impl() { | 
|  | 253 | if(initialized_) { | 
|  | 254 | initialized_ = false; | 
|  | 255 | int ret = pthread_rwlock_destroy(&rw_lock_); | 
|  | 256 | assert(ret == 0); | 
|  | 257 | } | 
|  | 258 | } | 
|  | 259 |  | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 260 | void acquireRead() const { | 
|  | 261 | PROFILE_MUTEX_START_LOCK(); | 
|  | 262 | pthread_rwlock_rdlock(&rw_lock_); | 
|  | 263 | PROFILE_MUTEX_NOT_LOCKED();  // not exclusive, so use not-locked path | 
|  | 264 | } | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 265 |  | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 266 | void acquireWrite() const { | 
|  | 267 | PROFILE_MUTEX_START_LOCK(); | 
|  | 268 | pthread_rwlock_wrlock(&rw_lock_); | 
|  | 269 | PROFILE_MUTEX_LOCKED(); | 
|  | 270 | } | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 271 |  | 
|  | 272 | bool attemptRead() const { return pthread_rwlock_tryrdlock(&rw_lock_); } | 
|  | 273 |  | 
|  | 274 | bool attemptWrite() const { return pthread_rwlock_trywrlock(&rw_lock_); } | 
|  | 275 |  | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 276 | void release() const { | 
|  | 277 | PROFILE_MUTEX_START_UNLOCK(); | 
|  | 278 | pthread_rwlock_unlock(&rw_lock_); | 
|  | 279 | PROFILE_MUTEX_UNLOCKED(); | 
|  | 280 | } | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 281 |  | 
| David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 282 | private: | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 283 | mutable pthread_rwlock_t rw_lock_; | 
|  | 284 | mutable bool initialized_; | 
| David Reiss | 7a2065d | 2010-03-09 05:20:04 +0000 | [diff] [blame] | 285 | #ifndef THRIFT_NO_CONTENTION_PROFILING | 
|  | 286 | mutable int64_t profileTime_; | 
|  | 287 | #endif | 
| boz | cce8184 | 2007-07-06 22:27:52 +0000 | [diff] [blame] | 288 | }; | 
|  | 289 |  | 
|  | 290 | ReadWriteMutex::ReadWriteMutex() : impl_(new ReadWriteMutex::impl()) {} | 
|  | 291 |  | 
|  | 292 | void ReadWriteMutex::acquireRead() const { impl_->acquireRead(); } | 
|  | 293 |  | 
|  | 294 | void ReadWriteMutex::acquireWrite() const { impl_->acquireWrite(); } | 
|  | 295 |  | 
|  | 296 | bool ReadWriteMutex::attemptRead() const { return impl_->attemptRead(); } | 
|  | 297 |  | 
|  | 298 | bool ReadWriteMutex::attemptWrite() const { return impl_->attemptWrite(); } | 
|  | 299 |  | 
|  | 300 | void ReadWriteMutex::release() const { impl_->release(); } | 
|  | 301 |  | 
| T Jake Luciani | b5e6221 | 2009-01-31 22:36:20 +0000 | [diff] [blame] | 302 | }}} // apache::thrift::concurrency | 
| Marc Slemko | 6694987 | 2006-07-15 01:52:39 +0000 | [diff] [blame] | 303 |  |