blob: 8d52452b7e1a6c17a82707948324d30fd94e894f [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
Marc Slemko66949872006-07-15 01:52:39 +000020#include "Mutex.h"
David Reiss4e19f192010-03-09 05:19:59 +000021#include "Util.h"
Marc Slemko66949872006-07-15 01:52:39 +000022
23#include <assert.h>
24#include <pthread.h>
David Reiss7a2065d2010-03-09 05:20:04 +000025#include <signal.h>
Marc Slemko66949872006-07-15 01:52:39 +000026
yunfang14542962007-10-03 22:59:41 +000027using boost::shared_ptr;
28
T Jake Lucianib5e62212009-01-31 22:36:20 +000029namespace apache { namespace thrift { namespace concurrency {
Marc Slemko66949872006-07-15 01:52:39 +000030
David Reiss7a2065d2010-03-09 05:20:04 +000031#ifndef THRIFT_NO_CONTENTION_PROFILING
32
33static sig_atomic_t mutexProfilingSampleRate = 0;
34static MutexWaitCallback mutexProfilingCallback = 0;
35
36volatile static sig_atomic_t mutexProfilingCounter = 0;
37
38void enableMutexProfiling(int32_t profilingSampleRate,
39 MutexWaitCallback callback) {
40 mutexProfilingSampleRate = profilingSampleRate;
41 mutexProfilingCallback = callback;
42}
43
44#define PROFILE_MUTEX_START_LOCK() \
45 int64_t _lock_startTime = maybeGetProfilingStartTime();
46
47#define PROFILE_MUTEX_NOT_LOCKED() \
48 do { \
49 if (_lock_startTime > 0) { \
50 int64_t endTime = Util::currentTimeUsec(); \
51 (*mutexProfilingCallback)(this, endTime - _lock_startTime); \
52 } \
53 } while (0)
54
55#define PROFILE_MUTEX_LOCKED() \
56 do { \
57 profileTime_ = _lock_startTime; \
58 if (profileTime_ > 0) { \
59 profileTime_ = Util::currentTimeUsec() - profileTime_; \
60 } \
61 } while (0)
62
63#define PROFILE_MUTEX_START_UNLOCK() \
64 int64_t _temp_profileTime = profileTime_; \
65 profileTime_ = 0;
66
67#define PROFILE_MUTEX_UNLOCKED() \
68 do { \
69 if (_temp_profileTime > 0) { \
70 (*mutexProfilingCallback)(this, _temp_profileTime); \
71 } \
72 } while (0)
73
74static inline int64_t maybeGetProfilingStartTime() {
75 if (mutexProfilingSampleRate && mutexProfilingCallback) {
76 // This block is unsynchronized, but should produce a reasonable sampling
77 // rate on most architectures. The main race conditions are the gap
78 // between the decrement and the test, the non-atomicity of decrement, and
79 // potential caching of different values at different CPUs.
80 //
81 // - if two decrements race, the likeliest result is that the counter
82 // decrements slowly (perhaps much more slowly) than intended.
83 //
84 // - many threads could potentially decrement before resetting the counter
85 // to its large value, causing each additional incoming thread to
86 // profile every call. This situation is unlikely to persist for long
87 // as the critical gap is quite short, but profiling could be bursty.
88 sig_atomic_t localValue = --mutexProfilingCounter;
89 if (localValue <= 0) {
90 mutexProfilingCounter = mutexProfilingSampleRate;
91 return Util::currentTimeUsec();
92 }
93 }
94
95 return 0;
96}
97
98#else
99# define PROFILE_MUTEX_START_LOCK()
100# define PROFILE_MUTEX_NOT_LOCKED()
101# define PROFILE_MUTEX_LOCKED()
102# define PROFILE_MUTEX_START_UNLOCK()
103# define PROFILE_MUTEX_UNLOCKED()
104#endif // THRIFT_NO_CONTENTION_PROFILING
105
David Reiss0c90f6f2008-02-06 22:18:40 +0000106/**
Mark Sleef5f2be42006-09-05 21:05:31 +0000107 * Implementation of Mutex class using POSIX mutex
108 *
Mark Sleef5f2be42006-09-05 21:05:31 +0000109 * @version $Id:$
110 */
Marc Slemko66949872006-07-15 01:52:39 +0000111class Mutex::impl {
Mark Sleef5f2be42006-09-05 21:05:31 +0000112 public:
David Reissc6dab612008-06-10 22:55:13 +0000113 impl(Initializer init) : initialized_(false) {
David Reiss7a2065d2010-03-09 05:20:04 +0000114#ifndef THRIFT_NO_CONTENTION_PROFILING
115 profileTime_ = 0;
116#endif
David Reissc6dab612008-06-10 22:55:13 +0000117 init(&pthread_mutex_);
Mark Slee2f6404d2006-10-10 01:37:40 +0000118 initialized_ = true;
Marc Slemko66949872006-07-15 01:52:39 +0000119 }
120
121 ~impl() {
Mark Slee2f6404d2006-10-10 01:37:40 +0000122 if (initialized_) {
123 initialized_ = false;
Aditya Agarwal9dc57402007-03-31 17:45:12 +0000124 int ret = pthread_mutex_destroy(&pthread_mutex_);
Aditya Agarwal3f234da2007-04-01 01:19:57 +0000125 assert(ret == 0);
Marc Slemko66949872006-07-15 01:52:39 +0000126 }
127 }
128
David Reiss7a2065d2010-03-09 05:20:04 +0000129 void lock() const {
130 PROFILE_MUTEX_START_LOCK();
131 pthread_mutex_lock(&pthread_mutex_);
132 PROFILE_MUTEX_LOCKED();
133 }
Marc Slemko66949872006-07-15 01:52:39 +0000134
boz5362e702007-08-15 20:55:36 +0000135 bool trylock() const { return (0 == pthread_mutex_trylock(&pthread_mutex_)); }
136
David Reiss4e19f192010-03-09 05:19:59 +0000137 bool timedlock(int64_t milliseconds) const {
David Reiss318a3282010-03-22 02:34:57 +0000138#if defined(_POSIX_TIMEOUTS) && _POSIX_TIMEOUTS >= 200112L
David Reiss7a2065d2010-03-09 05:20:04 +0000139 PROFILE_MUTEX_START_LOCK();
140
David Reiss4e19f192010-03-09 05:19:59 +0000141 struct timespec ts;
142 Util::toTimespec(ts, milliseconds);
David Reiss7a2065d2010-03-09 05:20:04 +0000143 int ret = pthread_mutex_timedlock(&pthread_mutex_, &ts);
144 if (ret == 0) {
145 PROFILE_MUTEX_LOCKED();
146 return true;
147 }
148
149 PROFILE_MUTEX_NOT_LOCKED();
150 return false;
David Reiss318a3282010-03-22 02:34:57 +0000151#else
152 // If pthread_mutex_timedlock isn't supported, the safest thing to do
153 // is just do a nonblocking trylock.
154 return trylock();
155#endif
David Reiss4e19f192010-03-09 05:19:59 +0000156 }
157
David Reiss7a2065d2010-03-09 05:20:04 +0000158 void unlock() const {
159 PROFILE_MUTEX_START_UNLOCK();
160 pthread_mutex_unlock(&pthread_mutex_);
161 PROFILE_MUTEX_UNLOCKED();
162 }
Marc Slemko66949872006-07-15 01:52:39 +0000163
David Reissb9db49c2010-03-09 05:19:30 +0000164 void* getUnderlyingImpl() const { return (void*) &pthread_mutex_; }
165
Mark Sleef5f2be42006-09-05 21:05:31 +0000166 private:
Mark Slee2f6404d2006-10-10 01:37:40 +0000167 mutable pthread_mutex_t pthread_mutex_;
168 mutable bool initialized_;
David Reiss7a2065d2010-03-09 05:20:04 +0000169#ifndef THRIFT_NO_CONTENTION_PROFILING
170 mutable int64_t profileTime_;
171#endif
Marc Slemko66949872006-07-15 01:52:39 +0000172};
173
David Reissc6dab612008-06-10 22:55:13 +0000174Mutex::Mutex(Initializer init) : impl_(new Mutex::impl(init)) {}
Marc Slemko66949872006-07-15 01:52:39 +0000175
David Reissb9db49c2010-03-09 05:19:30 +0000176void* Mutex::getUnderlyingImpl() const { return impl_->getUnderlyingImpl(); }
177
Mark Slee2f6404d2006-10-10 01:37:40 +0000178void Mutex::lock() const { impl_->lock(); }
Marc Slemko66949872006-07-15 01:52:39 +0000179
boz5362e702007-08-15 20:55:36 +0000180bool Mutex::trylock() const { return impl_->trylock(); }
181
David Reiss4e19f192010-03-09 05:19:59 +0000182bool Mutex::timedlock(int64_t ms) const { return impl_->timedlock(ms); }
183
Mark Slee2f6404d2006-10-10 01:37:40 +0000184void Mutex::unlock() const { impl_->unlock(); }
Marc Slemko66949872006-07-15 01:52:39 +0000185
David Reissc6dab612008-06-10 22:55:13 +0000186void Mutex::DEFAULT_INITIALIZER(void* arg) {
187 pthread_mutex_t* pthread_mutex = (pthread_mutex_t*)arg;
188 int ret = pthread_mutex_init(pthread_mutex, NULL);
189 assert(ret == 0);
190}
191
Roger Meier178f8f22010-10-25 12:36:04 +0000192#if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) || defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
David Reissc6dab612008-06-10 22:55:13 +0000193static void init_with_kind(pthread_mutex_t* mutex, int kind) {
194 pthread_mutexattr_t mutexattr;
195 int ret = pthread_mutexattr_init(&mutexattr);
196 assert(ret == 0);
197
198 // Apparently, this can fail. Should we really be aborting?
199 ret = pthread_mutexattr_settype(&mutexattr, kind);
200 assert(ret == 0);
201
202 ret = pthread_mutex_init(mutex, &mutexattr);
203 assert(ret == 0);
204
205 ret = pthread_mutexattr_destroy(&mutexattr);
206 assert(ret == 0);
207}
Roger Meier178f8f22010-10-25 12:36:04 +0000208#endif
David Reissc6dab612008-06-10 22:55:13 +0000209
210#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
211void Mutex::ADAPTIVE_INITIALIZER(void* arg) {
212 // From mysql source: mysys/my_thr_init.c
213 // Set mutex type to "fast" a.k.a "adaptive"
214 //
215 // In this case the thread may steal the mutex from some other thread
216 // that is waiting for the same mutex. This will save us some
217 // context switches but may cause a thread to 'starve forever' while
218 // waiting for the mutex (not likely if the code within the mutex is
219 // short).
220 init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_ADAPTIVE_NP);
221}
222#endif
223
224#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
225void Mutex::RECURSIVE_INITIALIZER(void* arg) {
226 init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_RECURSIVE_NP);
227}
228#endif
229
230
David Reiss0c90f6f2008-02-06 22:18:40 +0000231/**
bozcce81842007-07-06 22:27:52 +0000232 * Implementation of ReadWriteMutex class using POSIX rw lock
233 *
bozcce81842007-07-06 22:27:52 +0000234 * @version $Id:$
235 */
236class ReadWriteMutex::impl {
237public:
238 impl() : initialized_(false) {
David Reiss7a2065d2010-03-09 05:20:04 +0000239#ifndef THRIFT_NO_CONTENTION_PROFILING
240 profileTime_ = 0;
241#endif
bozcce81842007-07-06 22:27:52 +0000242 int ret = pthread_rwlock_init(&rw_lock_, NULL);
243 assert(ret == 0);
244 initialized_ = true;
245 }
246
247 ~impl() {
248 if(initialized_) {
249 initialized_ = false;
250 int ret = pthread_rwlock_destroy(&rw_lock_);
251 assert(ret == 0);
252 }
253 }
254
David Reiss7a2065d2010-03-09 05:20:04 +0000255 void acquireRead() const {
256 PROFILE_MUTEX_START_LOCK();
257 pthread_rwlock_rdlock(&rw_lock_);
258 PROFILE_MUTEX_NOT_LOCKED(); // not exclusive, so use not-locked path
259 }
bozcce81842007-07-06 22:27:52 +0000260
David Reiss7a2065d2010-03-09 05:20:04 +0000261 void acquireWrite() const {
262 PROFILE_MUTEX_START_LOCK();
263 pthread_rwlock_wrlock(&rw_lock_);
264 PROFILE_MUTEX_LOCKED();
265 }
bozcce81842007-07-06 22:27:52 +0000266
267 bool attemptRead() const { return pthread_rwlock_tryrdlock(&rw_lock_); }
268
269 bool attemptWrite() const { return pthread_rwlock_trywrlock(&rw_lock_); }
270
David Reiss7a2065d2010-03-09 05:20:04 +0000271 void release() const {
272 PROFILE_MUTEX_START_UNLOCK();
273 pthread_rwlock_unlock(&rw_lock_);
274 PROFILE_MUTEX_UNLOCKED();
275 }
bozcce81842007-07-06 22:27:52 +0000276
David Reiss0c90f6f2008-02-06 22:18:40 +0000277private:
bozcce81842007-07-06 22:27:52 +0000278 mutable pthread_rwlock_t rw_lock_;
279 mutable bool initialized_;
David Reiss7a2065d2010-03-09 05:20:04 +0000280#ifndef THRIFT_NO_CONTENTION_PROFILING
281 mutable int64_t profileTime_;
282#endif
bozcce81842007-07-06 22:27:52 +0000283};
284
285ReadWriteMutex::ReadWriteMutex() : impl_(new ReadWriteMutex::impl()) {}
286
287void ReadWriteMutex::acquireRead() const { impl_->acquireRead(); }
288
289void ReadWriteMutex::acquireWrite() const { impl_->acquireWrite(); }
290
291bool ReadWriteMutex::attemptRead() const { return impl_->attemptRead(); }
292
293bool ReadWriteMutex::attemptWrite() const { return impl_->attemptWrite(); }
294
295void ReadWriteMutex::release() const { impl_->release(); }
296
T Jake Lucianib5e62212009-01-31 22:36:20 +0000297}}} // apache::thrift::concurrency
Marc Slemko66949872006-07-15 01:52:39 +0000298