1 // Copyright 2020 The Marl Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Wrappers around std::mutex and std::unique_lock that provide clang's
16 // Thread Safety Analysis annotations.
17 // See: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
18 
19 #ifndef marl_mutex_h
20 #define marl_mutex_h
21 
22 #include "export.h"
23 #include "tsa.h"
24 
25 #include <condition_variable>
26 #include <mutex>
27 
28 namespace marl {
29 
30 // mutex is a wrapper around std::mutex that offers Thread Safety Analysis
31 // annotations.
32 // mutex also holds methods for performing std::condition_variable::wait() calls
33 // as these require a std::unique_lock<> which are unsupported by the TSA.
34 class CAPABILITY("mutex") mutex {
35  public:
lock()36   MARL_NO_EXPORT inline void lock() ACQUIRE() { _.lock(); }
37 
unlock()38   MARL_NO_EXPORT inline void unlock() RELEASE() { _.unlock(); }
39 
try_lock()40   MARL_NO_EXPORT inline bool try_lock() TRY_ACQUIRE(true) {
41     return _.try_lock();
42   }
43 
44   // wait_locked calls cv.wait() on this already locked mutex.
45   template <typename Predicate>
wait_locked(std::condition_variable & cv,Predicate && p)46   MARL_NO_EXPORT inline void wait_locked(std::condition_variable& cv,
47                                          Predicate&& p) REQUIRES(this) {
48     std::unique_lock<std::mutex> lock(_, std::adopt_lock);
49     cv.wait(lock, std::forward<Predicate>(p));
50     lock.release();  // Keep lock held.
51   }
52 
53   // wait_until_locked calls cv.wait() on this already locked mutex.
54   template <typename Predicate, typename Time>
wait_until_locked(std::condition_variable & cv,Time && time,Predicate && p)55   MARL_NO_EXPORT inline bool wait_until_locked(std::condition_variable& cv,
56                                                Time&& time,
57                                                Predicate&& p) REQUIRES(this) {
58     std::unique_lock<std::mutex> lock(_, std::adopt_lock);
59     auto res = cv.wait_until(lock, std::forward<Time>(time),
60                              std::forward<Predicate>(p));
61     lock.release();  // Keep lock held.
62     return res;
63   }
64 
65  private:
66   friend class lock;
67   std::mutex _;
68 };
69 
70 // lock is a RAII lock helper that offers Thread Safety Analysis annotations.
71 // lock also holds methods for performing std::condition_variable::wait()
72 // calls as these require a std::unique_lock<> which are unsupported by the TSA.
73 class SCOPED_CAPABILITY lock {
74  public:
lock(mutex & m)75   inline lock(mutex& m) ACQUIRE(m) : _(m._) {}
RELEASE()76   inline ~lock() RELEASE() {}
77 
78   // wait calls cv.wait() on this lock.
79   template <typename Predicate>
wait(std::condition_variable & cv,Predicate && p)80   inline void wait(std::condition_variable& cv, Predicate&& p) {
81     cv.wait(_, std::forward<Predicate>(p));
82   }
83 
84   // wait_until calls cv.wait() on this lock.
85   template <typename Predicate, typename Time>
wait_until(std::condition_variable & cv,Time && time,Predicate && p)86   inline bool wait_until(std::condition_variable& cv,
87                          Time&& time,
88                          Predicate&& p) {
89     return cv.wait_until(_, std::forward<Time>(time),
90                          std::forward<Predicate>(p));
91   }
92 
owns_lock()93   inline bool owns_lock() const { return _.owns_lock(); }
94 
95   // lock_no_tsa locks the mutex outside of the visiblity of the thread
96   // safety analysis. Use with caution.
lock_no_tsa()97   inline void lock_no_tsa() { _.lock(); }
98 
99   // unlock_no_tsa unlocks the mutex outside of the visiblity of the thread
100   // safety analysis. Use with caution.
unlock_no_tsa()101   inline void unlock_no_tsa() { _.unlock(); }
102 
103  private:
104   std::unique_lock<std::mutex> _;
105 };
106 
107 }  // namespace marl
108 
109 #endif  // marl_mutex_h
110