folly copyright 2015 -> copyright 2016
[folly.git] / folly / experimental / fibers / TimedMutex.h
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17
18 #include <pthread.h>
19
20 #include <folly/experimental/fibers/GenericBaton.h>
21
22 namespace folly { namespace fibers {
23
24 /**
25  * @class TimedMutex
26  *
27  * Like mutex but allows timed_lock in addition to lock and try_lock.
28  **/
29 template <typename BatonType>
30 class TimedMutex {
31  public:
32   TimedMutex() {
33     pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE);
34   }
35
36   ~TimedMutex() {
37     pthread_spin_destroy(&lock_);
38   }
39
40   TimedMutex(const TimedMutex& rhs) = delete;
41   TimedMutex& operator=(const TimedMutex& rhs) = delete;
42   TimedMutex(TimedMutex&& rhs) = delete;
43   TimedMutex& operator=(TimedMutex&& rhs) = delete;
44
45   // Lock the mutex. The thread / fiber is blocked until the mutex is free
46   void lock();
47
48   // Lock the mutex. The thread / fiber will be blocked for a time duration.
49   //
50   // @return        true if the mutex was locked, false otherwise
51   template <typename Rep, typename Period>
52   bool timed_lock(
53       const std::chrono::duration<Rep, Period>& duration);
54
55   // Try to obtain lock without blocking the thread or fiber
56   bool try_lock();
57
58   // Unlock the mutex and wake up a waiter if there is one
59   void unlock();
60
61  private:
62   typedef boost::intrusive::list_member_hook<> MutexWaiterHookType;
63
64   // represents a waiter waiting for the lock. The waiter waits on the
65   // baton until it is woken up by a post or timeout expires.
66   struct MutexWaiter {
67     BatonType baton;
68     MutexWaiterHookType hook;
69   };
70
71   typedef boost::intrusive::member_hook<MutexWaiter,
72                                         MutexWaiterHookType,
73                                         &MutexWaiter::hook> MutexWaiterHook;
74
75   typedef boost::intrusive::list<MutexWaiter,
76                                  MutexWaiterHook,
77                                  boost::intrusive::constant_time_size<true>>
78   MutexWaiterList;
79
80   pthread_spinlock_t lock_;         //< lock to protect waiter list
81   bool locked_ = false;             //< is this locked by some thread?
82   MutexWaiterList waiters_;         //< list of waiters
83 };
84
85 /**
86  * @class TimedRWMutex
87  *
88  * A readers-writer lock which allows multiple readers to hold the
89  * lock simultaneously or only one writer.
90  *
91  * NOTE: This is a reader-preferred RWLock i.e. readers are give priority
92  * when there are both readers and writers waiting to get the lock.
93  **/
94 template <typename BatonType>
95 class TimedRWMutex {
96  public:
97   TimedRWMutex() {
98     pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE);
99   }
100
101   ~TimedRWMutex() {
102     pthread_spin_destroy(&lock_);
103   }
104
105   TimedRWMutex(const TimedRWMutex& rhs) = delete;
106   TimedRWMutex& operator=(const TimedRWMutex& rhs) = delete;
107   TimedRWMutex(TimedRWMutex&& rhs) = delete;
108   TimedRWMutex& operator=(TimedRWMutex&& rhs) = delete;
109
110   // Lock for shared access. The thread / fiber is blocked until the lock
111   // can be acquired.
112   void read_lock();
113
114   // Like read_lock except the thread /fiber is blocked for a time duration
115   // @return        true if locked successfully, false otherwise.
116   template <typename Rep, typename Period>
117   bool timed_read_lock(const std::chrono::duration<Rep, Period>& duration);
118
119   // Like read_lock but doesn't block the thread / fiber if the lock can't
120   // be acquired.
121   // @return        true if lock was acquired, false otherwise.
122   bool try_read_lock();
123
124   // Obtain an exclusive lock. The thread / fiber is blocked until the lock
125   // is available.
126   void write_lock();
127
128   // Like write_lock except the thread / fiber is blocked for a time duration
129   // @return        true if locked successfully, false otherwise.
130   template <typename Rep, typename Period>
131   bool timed_write_lock(const std::chrono::duration<Rep, Period>& duration);
132
133   // Like write_lock but doesn't block the thread / fiber if the lock cant be
134   // obtained.
135   // @return        true if lock was acquired, false otherwise.
136   bool try_write_lock();
137
138   // Wrapper for write_lock() for compatibility with Mutex
139   void lock() { write_lock(); }
140
141   // Realease the lock. The thread / fiber will wake up all readers if there are
142   // any. If there are waiting writers then only one of them will be woken up.
143   // NOTE: readers are given priority over writers (see above comment)
144   void unlock();
145
146   // Downgrade the lock. The thread / fiber will wake up all readers if there
147   // are any.
148   void downgrade();
149
150   class ReadHolder {
151    public:
152     explicit ReadHolder(TimedRWMutex& lock)
153         : lock_(&lock) {
154       lock_->read_lock();
155     }
156
157     ~ReadHolder() {
158       if (lock_) {
159         lock_->unlock();
160       }
161     }
162
163     ReadHolder(const ReadHolder& rhs) = delete;
164     ReadHolder& operator=(const ReadHolder& rhs) = delete;
165     ReadHolder(ReadHolder&& rhs) = delete;
166     ReadHolder& operator=(ReadHolder&& rhs) = delete;
167
168    private:
169     TimedRWMutex* lock_;
170   };
171
172   class WriteHolder {
173    public:
174     explicit WriteHolder(TimedRWMutex& lock) : lock_(&lock) {
175       lock_->write_lock();
176     }
177
178     ~WriteHolder() {
179       if (lock_) {
180         lock_->unlock();
181       }
182     }
183
184     WriteHolder(const WriteHolder& rhs) = delete;
185     WriteHolder& operator=(const WriteHolder& rhs) = delete;
186     WriteHolder(WriteHolder&& rhs) = delete;
187     WriteHolder& operator=(WriteHolder&& rhs) = delete;
188
189    private:
190     TimedRWMutex* lock_;
191   };
192
193  private:
194   // invariants that must hold when the lock is not held by anyone
195   void verify_unlocked_properties() {
196     assert(readers_ == 0);
197     assert(read_waiters_.empty());
198     assert(write_waiters_.empty());
199   }
200
201   // Different states the lock can be in
202   enum class State {
203     UNLOCKED,
204     READ_LOCKED,
205     WRITE_LOCKED,
206   };
207
208   typedef boost::intrusive::list_member_hook<> MutexWaiterHookType;
209
210   // represents a waiter waiting for the lock.
211   struct MutexWaiter {
212     BatonType baton;
213     MutexWaiterHookType hook;
214   };
215
216   typedef boost::intrusive::member_hook<MutexWaiter,
217                                         MutexWaiterHookType,
218                                         &MutexWaiter::hook> MutexWaiterHook;
219
220   typedef boost::intrusive::list<MutexWaiter,
221                                  MutexWaiterHook,
222                                  boost::intrusive::constant_time_size<true>>
223   MutexWaiterList;
224
225   pthread_spinlock_t  lock_;            //< lock protecting the internal state
226                                         // (state_, read_waiters_, etc.)
227   State state_ = State::UNLOCKED;
228
229   uint32_t readers_ = 0;                //< Number of readers who have the lock
230
231   MutexWaiterList write_waiters_;       //< List of thread / fibers waiting for
232                                         //  exclusive access
233
234   MutexWaiterList read_waiters_;        //< List of thread / fibers waiting for
235                                         //  shared access
236 };
237
238 }}
239
240 #include "TimedMutex-inl.h"