blob: 3267c73b45bd86daf95010bc416413de94a1a4fb [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
Mark Sleef5f2be42006-09-05 21:05:31 +000020#ifndef _THRIFT_CONCURRENCY_MUTEX_H_
21#define _THRIFT_CONCURRENCY_MUTEX_H_ 1
Marc Slemko66949872006-07-15 01:52:39 +000022
boz19cee902007-09-22 23:08:11 +000023#include <boost/shared_ptr.hpp>
Bryan Duxburyc58c9e82011-09-01 16:52:56 +000024#include <boost/noncopyable.hpp>
boz19cee902007-09-22 23:08:11 +000025
T Jake Lucianib5e62212009-01-31 22:36:20 +000026namespace apache { namespace thrift { namespace concurrency {
Marc Slemko66949872006-07-15 01:52:39 +000027
David Reiss7a2065d2010-03-09 05:20:04 +000028#ifndef THRIFT_NO_CONTENTION_PROFILING
29
30/**
31 * Determines if the Thrift Mutex and ReadWriteMutex classes will attempt to
32 * profile their blocking acquire methods. If this value is set to non-zero,
33 * Thrift will attempt to invoke the callback once every profilingSampleRate
34 * times. However, as the sampling is not synchronized the rate is not
35 * guranateed, and could be subject to big bursts and swings. Please ensure
36 * your sampling callback is as performant as your application requires.
37 *
38 * The callback will get called with the wait time taken to lock the mutex in
39 * usec and a (void*) that uniquely identifies the Mutex (or ReadWriteMutex)
40 * being locked.
41 *
42 * The enableMutexProfiling() function is unsynchronized; calling this function
43 * while profiling is already enabled may result in race conditions. On
44 * architectures where a pointer assignment is atomic, this is safe but there
45 * is no guarantee threads will agree on a single callback within any
46 * particular time period.
47 */
48typedef void (*MutexWaitCallback)(const void* id, int64_t waitTimeMicros);
49void enableMutexProfiling(int32_t profilingSampleRate,
50 MutexWaitCallback callback);
51
52#endif
53
Mark Sleef5f2be42006-09-05 21:05:31 +000054/**
55 * A simple mutex class
56 *
Mark Sleef5f2be42006-09-05 21:05:31 +000057 * @version $Id:$
58 */
Marc Slemko66949872006-07-15 01:52:39 +000059class Mutex {
Marc Slemko66949872006-07-15 01:52:39 +000060 public:
David Reissc6dab612008-06-10 22:55:13 +000061 typedef void (*Initializer)(void*);
62
63 Mutex(Initializer init = DEFAULT_INITIALIZER);
boz19cee902007-09-22 23:08:11 +000064 virtual ~Mutex() {}
Marc Slemko66949872006-07-15 01:52:39 +000065 virtual void lock() const;
boz5362e702007-08-15 20:55:36 +000066 virtual bool trylock() const;
David Reiss4e19f192010-03-09 05:19:59 +000067 virtual bool timedlock(int64_t milliseconds) const;
Marc Slemko66949872006-07-15 01:52:39 +000068 virtual void unlock() const;
69
David Reissb9db49c2010-03-09 05:19:30 +000070 void* getUnderlyingImpl() const;
71
David Reissc6dab612008-06-10 22:55:13 +000072 static void DEFAULT_INITIALIZER(void*);
David Reissc6dab612008-06-10 22:55:13 +000073 static void ADAPTIVE_INITIALIZER(void*);
David Reissc6dab612008-06-10 22:55:13 +000074 static void RECURSIVE_INITIALIZER(void*);
David Reissc6dab612008-06-10 22:55:13 +000075
Marc Slemko66949872006-07-15 01:52:39 +000076 private:
boz19cee902007-09-22 23:08:11 +000077
Marc Slemko66949872006-07-15 01:52:39 +000078 class impl;
yunfang14542962007-10-03 22:59:41 +000079 boost::shared_ptr<impl> impl_;
Marc Slemko66949872006-07-15 01:52:39 +000080};
81
bozcce81842007-07-06 22:27:52 +000082class ReadWriteMutex {
83public:
84 ReadWriteMutex();
boz19cee902007-09-22 23:08:11 +000085 virtual ~ReadWriteMutex() {}
bozcce81842007-07-06 22:27:52 +000086
87 // these get the lock and block until it is done successfully
88 virtual void acquireRead() const;
89 virtual void acquireWrite() const;
90
91 // these attempt to get the lock, returning false immediately if they fail
92 virtual bool attemptRead() const;
93 virtual bool attemptWrite() const;
94
95 // this releases both read and write locks
96 virtual void release() const;
David Reiss0c90f6f2008-02-06 22:18:40 +000097
bozcce81842007-07-06 22:27:52 +000098private:
boz19cee902007-09-22 23:08:11 +000099
bozcce81842007-07-06 22:27:52 +0000100 class impl;
yunfang14542962007-10-03 22:59:41 +0000101 boost::shared_ptr<impl> impl_;
bozcce81842007-07-06 22:27:52 +0000102};
103
Bryan Duxburyc58c9e82011-09-01 16:52:56 +0000104class Guard : boost::noncopyable {
David Reiss0c90f6f2008-02-06 22:18:40 +0000105 public:
David Reiss4e19f192010-03-09 05:19:59 +0000106 Guard(const Mutex& value, int64_t timeout = 0) : mutex_(&value) {
107 if (timeout == 0) {
108 value.lock();
109 } else if (timeout < 0) {
110 if (!value.trylock()) {
111 mutex_ = NULL;
112 }
113 } else {
114 if (!value.timedlock(timeout)) {
115 mutex_ = NULL;
116 }
117 }
Marc Slemko66949872006-07-15 01:52:39 +0000118 }
bozcce81842007-07-06 22:27:52 +0000119 ~Guard() {
David Reiss4e19f192010-03-09 05:19:59 +0000120 if (mutex_) {
121 mutex_->unlock();
122 }
123 }
124
125 operator bool() const {
126 return (mutex_ != NULL);
Marc Slemko66949872006-07-15 01:52:39 +0000127 }
128
129 private:
David Reiss4e19f192010-03-09 05:19:59 +0000130 const Mutex* mutex_;
Marc Slemko66949872006-07-15 01:52:39 +0000131};
132
David Reisse96fa552010-03-09 05:19:37 +0000133// Can be used as second argument to RWGuard to make code more readable
134// as to whether we're doing acquireRead() or acquireWrite().
135enum RWGuardType {
136 RW_READ = 0,
Roger Meier0069cc42010-10-13 18:10:18 +0000137 RW_WRITE = 1
David Reisse96fa552010-03-09 05:19:37 +0000138};
139
140
yunfanga36f5db2007-07-14 01:23:05 +0000141class RWGuard {
David Reiss0c90f6f2008-02-06 22:18:40 +0000142 public:
David Reisse96fa552010-03-09 05:19:37 +0000143 RWGuard(const ReadWriteMutex& value, bool write = false)
144 : rw_mutex_(value) {
yunfanga36f5db2007-07-14 01:23:05 +0000145 if (write) {
146 rw_mutex_.acquireWrite();
David Reiss96d23882007-07-26 21:10:32 +0000147 } else {
yunfanga36f5db2007-07-14 01:23:05 +0000148 rw_mutex_.acquireRead();
David Reiss96d23882007-07-26 21:10:32 +0000149 }
David Reiss0c90f6f2008-02-06 22:18:40 +0000150 }
David Reisse96fa552010-03-09 05:19:37 +0000151
152 RWGuard(const ReadWriteMutex& value, RWGuardType type)
153 : rw_mutex_(value) {
154 if (type == RW_WRITE) {
155 rw_mutex_.acquireWrite();
156 } else {
157 rw_mutex_.acquireRead();
158 }
159 }
yunfanga36f5db2007-07-14 01:23:05 +0000160 ~RWGuard() {
161 rw_mutex_.release();
David Reiss0c90f6f2008-02-06 22:18:40 +0000162 }
163 private:
boz19cee902007-09-22 23:08:11 +0000164 const ReadWriteMutex& rw_mutex_;
David Reiss0c90f6f2008-02-06 22:18:40 +0000165};
yunfanga36f5db2007-07-14 01:23:05 +0000166
Marc Slemko66949872006-07-15 01:52:39 +0000167
David Reisseaa61e42007-12-20 21:42:05 +0000168// A little hack to prevent someone from trying to do "Guard(m);"
David Reissbe378f22009-05-07 00:41:18 +0000169// Such a use is invalid because the temporary Guard object is
170// destoryed at the end of the line, releasing the lock.
David Reisseaa61e42007-12-20 21:42:05 +0000171// Sorry for polluting the global namespace, but I think it's worth it.
172#define Guard(m) incorrect_use_of_Guard(m)
173#define RWGuard(m) incorrect_use_of_RWGuard(m)
174
175
T Jake Lucianib5e62212009-01-31 22:36:20 +0000176}}} // apache::thrift::concurrency
Marc Slemko66949872006-07-15 01:52:39 +0000177
Mark Sleef5f2be42006-09-05 21:05:31 +0000178#endif // #ifndef _THRIFT_CONCURRENCY_MUTEX_H_