xref: /openbmc/sdbusplus/include/sdbusplus/async/mutex.hpp (revision bd04c3b26ec121fdcf9715f2173ca986a01ab2e3)
1 #pragma once
2 
3 #include <sdbusplus/async/execution.hpp>
4 #include <sdbusplus/event.hpp>
5 
6 #include <queue>
7 #include <string>
8 
9 namespace sdbusplus::async
10 {
11 
12 namespace mutex_ns
13 {
14 struct mutex_completion;
15 } // namespace mutex_ns
16 
17 class lock_guard;
18 
19 class mutex
20 {
21   public:
22     mutex() = delete;
23     mutex(const mutex&) = delete;
24     mutex& operator=(const mutex&) = delete;
25     mutex(mutex&&) = delete;
26     mutex& operator=(mutex&&) = delete;
27     ~mutex() = default;
28 
29     mutex(const std::string& name = "sdbusplus::async::mutex");
30 
31     friend mutex_ns::mutex_completion;
32     friend lock_guard;
33 
34   private:
35     void unlock();
36 
37     std::string name;
38     bool locked{false};
39     std::queue<mutex_ns::mutex_completion*> waitingTasks;
40     std::mutex lock{};
41 };
42 
43 // RAII wrapper for mutex for the duration of a scoped block.
44 class lock_guard
45 {
46   public:
47     lock_guard() = delete;
48     lock_guard(const lock_guard&) = delete;
49     lock_guard& operator=(const lock_guard&) = delete;
50     lock_guard(lock_guard&&) = delete;
51     lock_guard& operator=(lock_guard&&) = delete;
52 
lock_guard(mutex & mutexInstance)53     explicit lock_guard(mutex& mutexInstance) : mutexInstance(mutexInstance) {}
54 
~lock_guard()55     ~lock_guard()
56     {
57         if (owned)
58         {
59             mutexInstance.unlock();
60             owned = false;
61         }
62     }
63 
64     auto lock() noexcept;
65     auto unlock() noexcept;
66 
67   private:
68     mutex& mutexInstance;
69     bool owned = false;
70 };
71 
72 namespace mutex_ns
73 {
74 
75 struct mutex_completion
76 {
77     mutex_completion() = delete;
78     mutex_completion(const mutex_completion&) = delete;
79     mutex_completion& operator=(const mutex_completion&) = delete;
80     mutex_completion(mutex_completion&&) = delete;
81     ~mutex_completion() = default;
82 
mutex_completionsdbusplus::async::mutex_ns::mutex_completion83     explicit mutex_completion(mutex& mutexInstance) noexcept :
84         mutexInstance(mutexInstance) {};
85 
86     friend mutex;
87 
tag_invoke(execution::start_t,mutex_completion & self)88     friend void tag_invoke(execution::start_t, mutex_completion& self) noexcept
89     {
90         self.arm();
91     }
92 
93   private:
94     virtual void complete() noexcept = 0;
95     virtual void stop() noexcept = 0;
96     void arm() noexcept;
97 
98     mutex& mutexInstance;
99 };
100 
101 // Implementation (templated based on Receiver) of mutex_completion.
102 template <execution::receiver Receiver>
103 struct mutex_operation : mutex_completion
104 {
mutex_operationsdbusplus::async::mutex_ns::mutex_operation105     mutex_operation(mutex& mutexInstance, Receiver r) :
106         mutex_completion(mutexInstance), receiver(std::move(r))
107     {}
108 
109   private:
completesdbusplus::async::mutex_ns::mutex_operation110     void complete() noexcept override final
111     {
112         execution::set_value(std::move(receiver));
113     }
114 
stopsdbusplus::async::mutex_ns::mutex_operation115     void stop() noexcept override final
116     {
117         execution::set_stopped(std::move(receiver));
118     }
119 
120     Receiver receiver;
121 };
122 
123 // mutex sender
124 struct mutex_sender
125 {
126     using is_sender = void;
127 
128     mutex_sender() = delete;
mutex_sendersdbusplus::async::mutex_ns::mutex_sender129     explicit mutex_sender(mutex& mutexInstance) noexcept :
130         mutexInstance(mutexInstance) {};
131 
132     friend auto tag_invoke(execution::get_completion_signatures_t,
133                            const mutex_sender&, auto)
134         -> execution::completion_signatures<execution::set_value_t(),
135                                             execution::set_stopped_t()>;
136 
137     template <execution::receiver R>
tag_invoke(execution::connect_t,mutex_sender && self,R r)138     friend auto tag_invoke(execution::connect_t, mutex_sender&& self, R r)
139         -> mutex_operation<R>
140     {
141         return {self.mutexInstance, std::move(r)};
142     }
143 
144   private:
145     mutex& mutexInstance;
146 };
147 
148 } // namespace mutex_ns
149 
lock()150 inline auto lock_guard::lock() noexcept
151 {
152     owned = true;
153     return mutex_ns::mutex_sender{this->mutexInstance};
154 }
155 
unlock()156 inline auto lock_guard::unlock() noexcept
157 {
158     if (owned)
159     {
160         mutexInstance.unlock();
161         owned = false;
162     }
163 }
164 
165 } // namespace sdbusplus::async
166