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