blob: 4f3f3232c747d829d417e7de526b932be66ce74d [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001/*
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 Slee9f0c6512007-02-28 23:58:26 +000019
Roger Meier2fa9c312011-09-05 19:15:53 +000020#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
Marc Slemko66949872006-07-15 01:52:39 +000023#include "Mutex.h"
David Reiss4e19f192010-03-09 05:19:59 +000024#include "Util.h"
Marc Slemko66949872006-07-15 01:52:39 +000025
26#include <assert.h>
Roger Meier2fa9c312011-09-05 19:15:53 +000027#ifdef HAVE_PTHREAD_H
Marc Slemko66949872006-07-15 01:52:39 +000028#include <pthread.h>
Roger Meier2fa9c312011-09-05 19:15:53 +000029#endif
David Reiss7a2065d2010-03-09 05:20:04 +000030#include <signal.h>
Marc Slemko66949872006-07-15 01:52:39 +000031
yunfang14542962007-10-03 22:59:41 +000032using boost::shared_ptr;
33
T Jake Lucianib5e62212009-01-31 22:36:20 +000034namespace apache { namespace thrift { namespace concurrency {
Marc Slemko66949872006-07-15 01:52:39 +000035
David Reiss7a2065d2010-03-09 05:20:04 +000036#ifndef THRIFT_NO_CONTENTION_PROFILING
37
38static sig_atomic_t mutexProfilingSampleRate = 0;
39static MutexWaitCallback mutexProfilingCallback = 0;
40
41volatile static sig_atomic_t mutexProfilingCounter = 0;
42
43void 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
79static 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 Reiss0c90f6f2008-02-06 22:18:40 +0000111/**
Mark Sleef5f2be42006-09-05 21:05:31 +0000112 * Implementation of Mutex class using POSIX mutex
113 *
Mark Sleef5f2be42006-09-05 21:05:31 +0000114 * @version $Id:$
115 */
Marc Slemko66949872006-07-15 01:52:39 +0000116class Mutex::impl {
Mark Sleef5f2be42006-09-05 21:05:31 +0000117 public:
David Reissc6dab612008-06-10 22:55:13 +0000118 impl(Initializer init) : initialized_(false) {
David Reiss7a2065d2010-03-09 05:20:04 +0000119#ifndef THRIFT_NO_CONTENTION_PROFILING
120 profileTime_ = 0;
121#endif
David Reissc6dab612008-06-10 22:55:13 +0000122 init(&pthread_mutex_);
Mark Slee2f6404d2006-10-10 01:37:40 +0000123 initialized_ = true;
Marc Slemko66949872006-07-15 01:52:39 +0000124 }
125
126 ~impl() {
Mark Slee2f6404d2006-10-10 01:37:40 +0000127 if (initialized_) {
128 initialized_ = false;
Aditya Agarwal9dc57402007-03-31 17:45:12 +0000129 int ret = pthread_mutex_destroy(&pthread_mutex_);
Aditya Agarwal3f234da2007-04-01 01:19:57 +0000130 assert(ret == 0);
Marc Slemko66949872006-07-15 01:52:39 +0000131 }
132 }
133
David Reiss7a2065d2010-03-09 05:20:04 +0000134 void lock() const {
135 PROFILE_MUTEX_START_LOCK();
136 pthread_mutex_lock(&pthread_mutex_);
137 PROFILE_MUTEX_LOCKED();
138 }
Marc Slemko66949872006-07-15 01:52:39 +0000139
boz5362e702007-08-15 20:55:36 +0000140 bool trylock() const { return (0 == pthread_mutex_trylock(&pthread_mutex_)); }
141
David Reiss4e19f192010-03-09 05:19:59 +0000142 bool timedlock(int64_t milliseconds) const {
David Reiss318a3282010-03-22 02:34:57 +0000143#if defined(_POSIX_TIMEOUTS) && _POSIX_TIMEOUTS >= 200112L
David Reiss7a2065d2010-03-09 05:20:04 +0000144 PROFILE_MUTEX_START_LOCK();
145
David Reiss4e19f192010-03-09 05:19:59 +0000146 struct timespec ts;
147 Util::toTimespec(ts, milliseconds);
David Reiss7a2065d2010-03-09 05:20:04 +0000148 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 Reiss318a3282010-03-22 02:34:57 +0000156#else
Roger Meierd0cdecf2011-12-08 19:34:01 +0000157 (void)milliseconds;
David Reiss318a3282010-03-22 02:34:57 +0000158 // If pthread_mutex_timedlock isn't supported, the safest thing to do
159 // is just do a nonblocking trylock.
160 return trylock();
161#endif
David Reiss4e19f192010-03-09 05:19:59 +0000162 }
163
David Reiss7a2065d2010-03-09 05:20:04 +0000164 void unlock() const {
165 PROFILE_MUTEX_START_UNLOCK();
166 pthread_mutex_unlock(&pthread_mutex_);
167 PROFILE_MUTEX_UNLOCKED();
168 }
Marc Slemko66949872006-07-15 01:52:39 +0000169
David Reissb9db49c2010-03-09 05:19:30 +0000170 void* getUnderlyingImpl() const { return (void*) &pthread_mutex_; }
171
Mark Sleef5f2be42006-09-05 21:05:31 +0000172 private:
Mark Slee2f6404d2006-10-10 01:37:40 +0000173 mutable pthread_mutex_t pthread_mutex_;
174 mutable bool initialized_;
David Reiss7a2065d2010-03-09 05:20:04 +0000175#ifndef THRIFT_NO_CONTENTION_PROFILING
176 mutable int64_t profileTime_;
177#endif
Marc Slemko66949872006-07-15 01:52:39 +0000178};
179
David Reissc6dab612008-06-10 22:55:13 +0000180Mutex::Mutex(Initializer init) : impl_(new Mutex::impl(init)) {}
Marc Slemko66949872006-07-15 01:52:39 +0000181
David Reissb9db49c2010-03-09 05:19:30 +0000182void* Mutex::getUnderlyingImpl() const { return impl_->getUnderlyingImpl(); }
183
Mark Slee2f6404d2006-10-10 01:37:40 +0000184void Mutex::lock() const { impl_->lock(); }
Marc Slemko66949872006-07-15 01:52:39 +0000185
boz5362e702007-08-15 20:55:36 +0000186bool Mutex::trylock() const { return impl_->trylock(); }
187
David Reiss4e19f192010-03-09 05:19:59 +0000188bool Mutex::timedlock(int64_t ms) const { return impl_->timedlock(ms); }
189
Mark Slee2f6404d2006-10-10 01:37:40 +0000190void Mutex::unlock() const { impl_->unlock(); }
Marc Slemko66949872006-07-15 01:52:39 +0000191
David Reissc6dab612008-06-10 22:55:13 +0000192void Mutex::DEFAULT_INITIALIZER(void* arg) {
193 pthread_mutex_t* pthread_mutex = (pthread_mutex_t*)arg;
194 int ret = pthread_mutex_init(pthread_mutex, NULL);
195 assert(ret == 0);
196}
197
Roger Meier178f8f22010-10-25 12:36:04 +0000198#if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) || defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
David Reissc6dab612008-06-10 22:55:13 +0000199static void init_with_kind(pthread_mutex_t* mutex, int kind) {
200 pthread_mutexattr_t mutexattr;
201 int ret = pthread_mutexattr_init(&mutexattr);
202 assert(ret == 0);
203
204 // Apparently, this can fail. Should we really be aborting?
205 ret = pthread_mutexattr_settype(&mutexattr, kind);
206 assert(ret == 0);
207
208 ret = pthread_mutex_init(mutex, &mutexattr);
209 assert(ret == 0);
210
211 ret = pthread_mutexattr_destroy(&mutexattr);
212 assert(ret == 0);
213}
Roger Meier178f8f22010-10-25 12:36:04 +0000214#endif
David Reissc6dab612008-06-10 22:55:13 +0000215
216#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
217void Mutex::ADAPTIVE_INITIALIZER(void* arg) {
218 // From mysql source: mysys/my_thr_init.c
219 // Set mutex type to "fast" a.k.a "adaptive"
220 //
221 // In this case the thread may steal the mutex from some other thread
222 // that is waiting for the same mutex. This will save us some
223 // context switches but may cause a thread to 'starve forever' while
224 // waiting for the mutex (not likely if the code within the mutex is
225 // short).
226 init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_ADAPTIVE_NP);
227}
228#endif
229
230#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
231void Mutex::RECURSIVE_INITIALIZER(void* arg) {
232 init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_RECURSIVE_NP);
233}
234#endif
235
236
David Reiss0c90f6f2008-02-06 22:18:40 +0000237/**
bozcce81842007-07-06 22:27:52 +0000238 * Implementation of ReadWriteMutex class using POSIX rw lock
239 *
bozcce81842007-07-06 22:27:52 +0000240 * @version $Id:$
241 */
242class ReadWriteMutex::impl {
243public:
244 impl() : initialized_(false) {
David Reiss7a2065d2010-03-09 05:20:04 +0000245#ifndef THRIFT_NO_CONTENTION_PROFILING
246 profileTime_ = 0;
247#endif
bozcce81842007-07-06 22:27:52 +0000248 int ret = pthread_rwlock_init(&rw_lock_, NULL);
249 assert(ret == 0);
250 initialized_ = true;
251 }
252
253 ~impl() {
254 if(initialized_) {
255 initialized_ = false;
256 int ret = pthread_rwlock_destroy(&rw_lock_);
257 assert(ret == 0);
258 }
259 }
260
David Reiss7a2065d2010-03-09 05:20:04 +0000261 void acquireRead() const {
262 PROFILE_MUTEX_START_LOCK();
263 pthread_rwlock_rdlock(&rw_lock_);
264 PROFILE_MUTEX_NOT_LOCKED(); // not exclusive, so use not-locked path
265 }
bozcce81842007-07-06 22:27:52 +0000266
David Reiss7a2065d2010-03-09 05:20:04 +0000267 void acquireWrite() const {
268 PROFILE_MUTEX_START_LOCK();
269 pthread_rwlock_wrlock(&rw_lock_);
270 PROFILE_MUTEX_LOCKED();
271 }
bozcce81842007-07-06 22:27:52 +0000272
Roger Meier3fc48192011-12-11 21:05:35 +0000273 bool attemptRead() const { return !pthread_rwlock_tryrdlock(&rw_lock_); }
bozcce81842007-07-06 22:27:52 +0000274
Roger Meier3fc48192011-12-11 21:05:35 +0000275 bool attemptWrite() const { return !pthread_rwlock_trywrlock(&rw_lock_); }
bozcce81842007-07-06 22:27:52 +0000276
David Reiss7a2065d2010-03-09 05:20:04 +0000277 void release() const {
278 PROFILE_MUTEX_START_UNLOCK();
279 pthread_rwlock_unlock(&rw_lock_);
280 PROFILE_MUTEX_UNLOCKED();
281 }
bozcce81842007-07-06 22:27:52 +0000282
David Reiss0c90f6f2008-02-06 22:18:40 +0000283private:
bozcce81842007-07-06 22:27:52 +0000284 mutable pthread_rwlock_t rw_lock_;
285 mutable bool initialized_;
David Reiss7a2065d2010-03-09 05:20:04 +0000286#ifndef THRIFT_NO_CONTENTION_PROFILING
287 mutable int64_t profileTime_;
288#endif
bozcce81842007-07-06 22:27:52 +0000289};
290
291ReadWriteMutex::ReadWriteMutex() : impl_(new ReadWriteMutex::impl()) {}
292
293void ReadWriteMutex::acquireRead() const { impl_->acquireRead(); }
294
295void ReadWriteMutex::acquireWrite() const { impl_->acquireWrite(); }
296
297bool ReadWriteMutex::attemptRead() const { return impl_->attemptRead(); }
298
299bool ReadWriteMutex::attemptWrite() const { return impl_->attemptWrite(); }
300
301void ReadWriteMutex::release() const { impl_->release(); }
302
Roger Meier3fc48192011-12-11 21:05:35 +0000303NoStarveReadWriteMutex::NoStarveReadWriteMutex() : writerWaiting_(false) {}
304
305void NoStarveReadWriteMutex::acquireRead() const
306{
307 if (writerWaiting_) {
308 // writer is waiting, block on the writer's mutex until he's done with it
309 mutex_.lock();
310 mutex_.unlock();
311 }
312
313 ReadWriteMutex::acquireRead();
314}
315
316void NoStarveReadWriteMutex::acquireWrite() const
317{
318 // if we can acquire the rwlock the easy way, we're done
319 if (attemptWrite()) {
320 return;
321 }
322
323 // failed to get the rwlock, do it the hard way:
324 // locking the mutex and setting writerWaiting will cause all new readers to
325 // block on the mutex rather than on the rwlock.
326 mutex_.lock();
327 writerWaiting_ = true;
328 ReadWriteMutex::acquireWrite();
329 writerWaiting_ = false;
330 mutex_.unlock();
331}
332
T Jake Lucianib5e62212009-01-31 22:36:20 +0000333}}} // apache::thrift::concurrency
Marc Slemko66949872006-07-15 01:52:39 +0000334