blob: d9921aa79fe1a733d7179c175b1aa630b8c6107d [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
Konrad Grochowski9be4e682013-06-22 22:03:31 +020020#include <thrift/thrift-config.h>
21
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -040022#include <thrift/Thrift.h>
Roger Meier4285ba22013-06-10 21:17:23 +020023#include <thrift/concurrency/Mutex.h>
24#include <thrift/concurrency/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
Konrad Grochowski16a23a62014-11-13 15:33:38 +010034namespace apache {
35namespace thrift {
36namespace concurrency {
Marc Slemko66949872006-07-15 01:52:39 +000037
David Reiss7a2065d2010-03-09 05:20:04 +000038#ifndef THRIFT_NO_CONTENTION_PROFILING
39
40static sig_atomic_t mutexProfilingSampleRate = 0;
41static MutexWaitCallback mutexProfilingCallback = 0;
42
43volatile static sig_atomic_t mutexProfilingCounter = 0;
44
Konrad Grochowski16a23a62014-11-13 15:33:38 +010045void enableMutexProfiling(int32_t profilingSampleRate, MutexWaitCallback callback) {
David Reiss7a2065d2010-03-09 05:20:04 +000046 mutexProfilingSampleRate = profilingSampleRate;
47 mutexProfilingCallback = callback;
48}
49
Konrad Grochowski16a23a62014-11-13 15:33:38 +010050#define PROFILE_MUTEX_START_LOCK() int64_t _lock_startTime = maybeGetProfilingStartTime();
David Reiss7a2065d2010-03-09 05:20:04 +000051
Konrad Grochowski16a23a62014-11-13 15:33:38 +010052#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 } \
David Reiss7a2065d2010-03-09 05:20:04 +000058 } while (0)
59
Konrad Grochowski16a23a62014-11-13 15:33:38 +010060#define PROFILE_MUTEX_LOCKED() \
61 do { \
62 profileTime_ = _lock_startTime; \
63 if (profileTime_ > 0) { \
64 profileTime_ = Util::currentTimeUsec() - profileTime_; \
65 } \
David Reiss7a2065d2010-03-09 05:20:04 +000066 } while (0)
67
Konrad Grochowski16a23a62014-11-13 15:33:38 +010068#define PROFILE_MUTEX_START_UNLOCK() \
69 int64_t _temp_profileTime = profileTime_; \
David Reiss7a2065d2010-03-09 05:20:04 +000070 profileTime_ = 0;
71
Konrad Grochowski16a23a62014-11-13 15:33:38 +010072#define PROFILE_MUTEX_UNLOCKED() \
73 do { \
74 if (_temp_profileTime > 0) { \
75 (*mutexProfilingCallback)(this, _temp_profileTime); \
76 } \
David Reiss7a2065d2010-03-09 05:20:04 +000077 } 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
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100104#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()
David Reiss7a2065d2010-03-09 05:20:04 +0000109#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 {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100117public:
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_);
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400130 THRIFT_UNUSED_VARIABLE(ret);
Aditya Agarwal3f234da2007-04-01 01:19:57 +0000131 assert(ret == 0);
Marc Slemko66949872006-07-15 01:52:39 +0000132 }
133 }
134
David Reiss7a2065d2010-03-09 05:20:04 +0000135 void lock() const {
136 PROFILE_MUTEX_START_LOCK();
137 pthread_mutex_lock(&pthread_mutex_);
138 PROFILE_MUTEX_LOCKED();
139 }
Marc Slemko66949872006-07-15 01:52:39 +0000140
boz5362e702007-08-15 20:55:36 +0000141 bool trylock() const { return (0 == pthread_mutex_trylock(&pthread_mutex_)); }
142
David Reiss4e19f192010-03-09 05:19:59 +0000143 bool timedlock(int64_t milliseconds) const {
David Reiss318a3282010-03-22 02:34:57 +0000144#if defined(_POSIX_TIMEOUTS) && _POSIX_TIMEOUTS >= 200112L
David Reiss7a2065d2010-03-09 05:20:04 +0000145 PROFILE_MUTEX_START_LOCK();
146
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400147 struct THRIFT_TIMESPEC ts;
Roger Meierf9f841d2012-06-19 20:42:33 +0000148 Util::toTimespec(ts, milliseconds + Util::currentTime());
David Reiss7a2065d2010-03-09 05:20:04 +0000149 int ret = pthread_mutex_timedlock(&pthread_mutex_, &ts);
150 if (ret == 0) {
151 PROFILE_MUTEX_LOCKED();
152 return true;
153 }
154
155 PROFILE_MUTEX_NOT_LOCKED();
156 return false;
David Reiss318a3282010-03-22 02:34:57 +0000157#else
Roger Meierf9f841d2012-06-19 20:42:33 +0000158 /* Otherwise follow solution used by Mono for Android */
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400159 struct THRIFT_TIMESPEC sleepytime, now, to;
Roger Meierf9f841d2012-06-19 20:42:33 +0000160
161 /* This is just to avoid a completely busy wait */
162 sleepytime.tv_sec = 0;
163 sleepytime.tv_nsec = 10000000L; /* 10ms */
164
165 Util::toTimespec(to, milliseconds + Util::currentTime());
166
167 while ((trylock()) == false) {
168 Util::toTimespec(now, Util::currentTime());
169 if (now.tv_sec >= to.tv_sec && now.tv_nsec >= to.tv_nsec) {
170 return false;
171 }
172 nanosleep(&sleepytime, NULL);
173 }
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400174
Roger Meierf9f841d2012-06-19 20:42:33 +0000175 return true;
David Reiss318a3282010-03-22 02:34:57 +0000176#endif
David Reiss4e19f192010-03-09 05:19:59 +0000177 }
178
David Reiss7a2065d2010-03-09 05:20:04 +0000179 void unlock() const {
180 PROFILE_MUTEX_START_UNLOCK();
181 pthread_mutex_unlock(&pthread_mutex_);
182 PROFILE_MUTEX_UNLOCKED();
183 }
Marc Slemko66949872006-07-15 01:52:39 +0000184
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100185 void* getUnderlyingImpl() const { return (void*)&pthread_mutex_; }
David Reissb9db49c2010-03-09 05:19:30 +0000186
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100187private:
Mark Slee2f6404d2006-10-10 01:37:40 +0000188 mutable pthread_mutex_t pthread_mutex_;
189 mutable bool initialized_;
David Reiss7a2065d2010-03-09 05:20:04 +0000190#ifndef THRIFT_NO_CONTENTION_PROFILING
191 mutable int64_t profileTime_;
192#endif
Marc Slemko66949872006-07-15 01:52:39 +0000193};
194
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100195Mutex::Mutex(Initializer init) : impl_(new Mutex::impl(init)) {
196}
Marc Slemko66949872006-07-15 01:52:39 +0000197
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100198void* Mutex::getUnderlyingImpl() const {
199 return impl_->getUnderlyingImpl();
200}
David Reissb9db49c2010-03-09 05:19:30 +0000201
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100202void Mutex::lock() const {
203 impl_->lock();
204}
Marc Slemko66949872006-07-15 01:52:39 +0000205
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100206bool Mutex::trylock() const {
207 return impl_->trylock();
208}
boz5362e702007-08-15 20:55:36 +0000209
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100210bool Mutex::timedlock(int64_t ms) const {
211 return impl_->timedlock(ms);
212}
David Reiss4e19f192010-03-09 05:19:59 +0000213
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100214void Mutex::unlock() const {
215 impl_->unlock();
216}
Marc Slemko66949872006-07-15 01:52:39 +0000217
David Reissc6dab612008-06-10 22:55:13 +0000218void Mutex::DEFAULT_INITIALIZER(void* arg) {
219 pthread_mutex_t* pthread_mutex = (pthread_mutex_t*)arg;
220 int ret = pthread_mutex_init(pthread_mutex, NULL);
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400221 THRIFT_UNUSED_VARIABLE(ret);
David Reissc6dab612008-06-10 22:55:13 +0000222 assert(ret == 0);
223}
224
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100225#if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) \
226 || defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
David Reissc6dab612008-06-10 22:55:13 +0000227static void init_with_kind(pthread_mutex_t* mutex, int kind) {
228 pthread_mutexattr_t mutexattr;
229 int ret = pthread_mutexattr_init(&mutexattr);
230 assert(ret == 0);
231
232 // Apparently, this can fail. Should we really be aborting?
233 ret = pthread_mutexattr_settype(&mutexattr, kind);
234 assert(ret == 0);
235
236 ret = pthread_mutex_init(mutex, &mutexattr);
237 assert(ret == 0);
238
239 ret = pthread_mutexattr_destroy(&mutexattr);
240 assert(ret == 0);
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400241 THRIFT_UNUSED_VARIABLE(ret);
David Reissc6dab612008-06-10 22:55:13 +0000242}
Roger Meier178f8f22010-10-25 12:36:04 +0000243#endif
David Reissc6dab612008-06-10 22:55:13 +0000244
245#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
246void Mutex::ADAPTIVE_INITIALIZER(void* arg) {
247 // From mysql source: mysys/my_thr_init.c
248 // Set mutex type to "fast" a.k.a "adaptive"
249 //
250 // In this case the thread may steal the mutex from some other thread
251 // that is waiting for the same mutex. This will save us some
252 // context switches but may cause a thread to 'starve forever' while
253 // waiting for the mutex (not likely if the code within the mutex is
254 // short).
255 init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_ADAPTIVE_NP);
256}
257#endif
258
259#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
260void Mutex::RECURSIVE_INITIALIZER(void* arg) {
261 init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_RECURSIVE_NP);
262}
263#endif
264
David Reiss0c90f6f2008-02-06 22:18:40 +0000265/**
bozcce81842007-07-06 22:27:52 +0000266 * Implementation of ReadWriteMutex class using POSIX rw lock
267 *
bozcce81842007-07-06 22:27:52 +0000268 * @version $Id:$
269 */
270class ReadWriteMutex::impl {
271public:
272 impl() : initialized_(false) {
David Reiss7a2065d2010-03-09 05:20:04 +0000273#ifndef THRIFT_NO_CONTENTION_PROFILING
274 profileTime_ = 0;
275#endif
bozcce81842007-07-06 22:27:52 +0000276 int ret = pthread_rwlock_init(&rw_lock_, NULL);
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400277 THRIFT_UNUSED_VARIABLE(ret);
bozcce81842007-07-06 22:27:52 +0000278 assert(ret == 0);
279 initialized_ = true;
280 }
281
282 ~impl() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100283 if (initialized_) {
bozcce81842007-07-06 22:27:52 +0000284 initialized_ = false;
285 int ret = pthread_rwlock_destroy(&rw_lock_);
Carl Yeksigian7cb7fc82013-06-07 07:33:01 -0400286 THRIFT_UNUSED_VARIABLE(ret);
bozcce81842007-07-06 22:27:52 +0000287 assert(ret == 0);
288 }
289 }
290
David Reiss7a2065d2010-03-09 05:20:04 +0000291 void acquireRead() const {
292 PROFILE_MUTEX_START_LOCK();
293 pthread_rwlock_rdlock(&rw_lock_);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100294 PROFILE_MUTEX_NOT_LOCKED(); // not exclusive, so use not-locked path
David Reiss7a2065d2010-03-09 05:20:04 +0000295 }
bozcce81842007-07-06 22:27:52 +0000296
David Reiss7a2065d2010-03-09 05:20:04 +0000297 void acquireWrite() const {
298 PROFILE_MUTEX_START_LOCK();
299 pthread_rwlock_wrlock(&rw_lock_);
300 PROFILE_MUTEX_LOCKED();
301 }
bozcce81842007-07-06 22:27:52 +0000302
Roger Meier3fc48192011-12-11 21:05:35 +0000303 bool attemptRead() const { return !pthread_rwlock_tryrdlock(&rw_lock_); }
bozcce81842007-07-06 22:27:52 +0000304
Roger Meier3fc48192011-12-11 21:05:35 +0000305 bool attemptWrite() const { return !pthread_rwlock_trywrlock(&rw_lock_); }
bozcce81842007-07-06 22:27:52 +0000306
David Reiss7a2065d2010-03-09 05:20:04 +0000307 void release() const {
308 PROFILE_MUTEX_START_UNLOCK();
309 pthread_rwlock_unlock(&rw_lock_);
310 PROFILE_MUTEX_UNLOCKED();
311 }
bozcce81842007-07-06 22:27:52 +0000312
David Reiss0c90f6f2008-02-06 22:18:40 +0000313private:
bozcce81842007-07-06 22:27:52 +0000314 mutable pthread_rwlock_t rw_lock_;
315 mutable bool initialized_;
David Reiss7a2065d2010-03-09 05:20:04 +0000316#ifndef THRIFT_NO_CONTENTION_PROFILING
317 mutable int64_t profileTime_;
318#endif
bozcce81842007-07-06 22:27:52 +0000319};
320
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100321ReadWriteMutex::ReadWriteMutex() : impl_(new ReadWriteMutex::impl()) {
322}
bozcce81842007-07-06 22:27:52 +0000323
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100324void ReadWriteMutex::acquireRead() const {
325 impl_->acquireRead();
326}
bozcce81842007-07-06 22:27:52 +0000327
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100328void ReadWriteMutex::acquireWrite() const {
329 impl_->acquireWrite();
330}
bozcce81842007-07-06 22:27:52 +0000331
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100332bool ReadWriteMutex::attemptRead() const {
333 return impl_->attemptRead();
334}
bozcce81842007-07-06 22:27:52 +0000335
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100336bool ReadWriteMutex::attemptWrite() const {
337 return impl_->attemptWrite();
338}
bozcce81842007-07-06 22:27:52 +0000339
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100340void ReadWriteMutex::release() const {
341 impl_->release();
342}
bozcce81842007-07-06 22:27:52 +0000343
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100344NoStarveReadWriteMutex::NoStarveReadWriteMutex() : writerWaiting_(false) {
345}
Roger Meier3fc48192011-12-11 21:05:35 +0000346
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100347void NoStarveReadWriteMutex::acquireRead() const {
Roger Meier3fc48192011-12-11 21:05:35 +0000348 if (writerWaiting_) {
349 // writer is waiting, block on the writer's mutex until he's done with it
350 mutex_.lock();
351 mutex_.unlock();
352 }
353
354 ReadWriteMutex::acquireRead();
355}
356
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100357void NoStarveReadWriteMutex::acquireWrite() const {
Roger Meier3fc48192011-12-11 21:05:35 +0000358 // if we can acquire the rwlock the easy way, we're done
359 if (attemptWrite()) {
360 return;
361 }
362
363 // failed to get the rwlock, do it the hard way:
364 // locking the mutex and setting writerWaiting will cause all new readers to
365 // block on the mutex rather than on the rwlock.
366 mutex_.lock();
367 writerWaiting_ = true;
368 ReadWriteMutex::acquireWrite();
369 writerWaiting_ = false;
370 mutex_.unlock();
371}
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100372}
373}
374} // apache::thrift::concurrency