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
28namespace 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.
34class CAPABILITY("mutex") mutex {
35 public:
36 MARL_NO_EXPORT inline void lock() ACQUIRE() { _.lock(); }
37
38 MARL_NO_EXPORT inline void unlock() RELEASE() { _.unlock(); }
39
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>
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>
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.
73class SCOPED_CAPABILITY lock {
74 public:
75 inline lock(mutex& m) ACQUIRE(m) : _(m._) {}
76 inline ~lock() RELEASE() {}
77
78 // wait calls cv.wait() on this lock.
79 template <typename Predicate>
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>
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
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.
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.
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