1 #pragma once
2 #include <sdbusplus/async/context.hpp>
3 #include <sdbusplus/async/execution.hpp>
4 #include <sdbusplus/bus/match.hpp>
5 #include <sdbusplus/message.hpp>
6 #include <sdbusplus/slot.hpp>
7
8 #include <condition_variable>
9 #include <mutex>
10 #include <queue>
11 #include <string_view>
12
13 namespace sdbusplus::async
14 {
15 namespace match_ns
16 {
17 struct match_completion;
18 }
19
20 /** Generator of dbus match Senders.
21 *
22 * This class registers a signal match pattern with the dbus and generates
23 * Senders using `next` to await the next matching signal.
24 */
25 class match : private bus::details::bus_friend
26 {
27 public:
28 match() = delete;
29 match(const match&) = delete;
30 match& operator=(const match&) = delete;
31 match(match&&) = delete;
32 match& operator=(match&&) = delete;
33 ~match();
34
35 /** Construct the match using the `pattern` string on the bus managed by the
36 * context. */
37 match(context& ctx, const std::string_view& pattern);
38
39 /** Get the Sender for the next event (as message).
40 *
41 * Note: the implementation only supports a single awaiting task. Two
42 * tasks should not share this object and both call `next`.
43 */
44 auto next() noexcept;
45
46 /** Get the Sender for the next event, which yields one of:
47 * void, Rs, tuple<Rs...>
48 *
49 * Note: the implementation only supports a single awaiting task. Two
50 * tasks should not share this object and both call `next`.
51 */
52 template <typename... Rs>
53 auto next() noexcept;
54
55 friend match_ns::match_completion;
56
57 private:
58 sdbusplus::slot_t slot;
59
60 std::mutex lock{};
61 std::queue<sdbusplus::message_t> queue{};
62 match_ns::match_completion* complete = nullptr;
63
64 /** Handle an incoming match event. */
65 void handle_match(message_t&&) noexcept;
66
67 /** Signal completion if there is an awaiting Receiver.
68 *
69 * This must be called with `lock` held (and ownership transfers).
70 */
71 void handle_completion(std::unique_lock<std::mutex>&&) noexcept;
72
73 slot_t makeMatch(context& ctx, const std::string_view& pattern);
74 };
75
76 namespace match_ns
77 {
78 // Virtual class to handle the match Receiver completions.
79 struct match_completion
80 {
81 match_completion() = delete;
82 match_completion(match_completion&&) = delete;
83
match_completionsdbusplus::async::match_ns::match_completion84 explicit match_completion(match& m) : m(m) {};
85 virtual ~match_completion() = default;
86
87 friend match;
88
tag_invoke(execution::start_t,match_completion & self)89 friend void tag_invoke(execution::start_t, match_completion& self) noexcept
90 {
91 self.arm();
92 }
93
94 private:
95 virtual void complete(message_t&&) noexcept = 0;
96 virtual void stop() noexcept = 0;
97 void arm() noexcept;
98
99 match& m;
100 };
101
102 // Implementation (templated based on Receiver) of match_completion.
103 template <execution::receiver Receiver>
104 struct match_operation : match_completion
105 {
match_operationsdbusplus::async::match_ns::match_operation106 match_operation(match& m, Receiver r) :
107 match_completion(m), receiver(std::move(r))
108 {}
109
110 private:
completesdbusplus::async::match_ns::match_operation111 void complete(message_t&& msg) noexcept override final
112 {
113 execution::set_value(std::move(receiver), std::move(msg));
114 }
115
stopsdbusplus::async::match_ns::match_operation116 void stop() noexcept override final
117 {
118 execution::set_stopped(std::move(receiver));
119 }
120
121 Receiver receiver;
122 };
123
124 // match Sender implementation.
125 struct match_sender
126 {
127 using is_sender = void;
128
129 match_sender() = delete;
match_sendersdbusplus::async::match_ns::match_sender130 explicit match_sender(match& m) noexcept : m(m) {};
131
132 friend auto tag_invoke(execution::get_completion_signatures_t,
133 const match_sender&, auto)
134 -> execution::completion_signatures<execution::set_value_t(message_t),
135 execution::set_stopped_t()>;
136
137 template <execution::receiver R>
tag_invoke(execution::connect_t,match_sender && self,R r)138 friend auto tag_invoke(execution::connect_t, match_sender&& self, R r)
139 -> match_operation<R>
140 {
141 return {self.m, std::move(r)};
142 }
143
144 private:
145 match& m;
146 };
147
148 }; // namespace match_ns
149
next()150 inline auto match::next() noexcept
151 {
152 return match_ns::match_sender(*this);
153 }
154
155 template <typename... Rs>
next()156 auto match::next() noexcept
157 {
158 return match_ns::match_sender(*this) |
159 execution::then([](message_t&& m) { return m.unpack<Rs...>(); });
160 }
161
162 } // namespace sdbusplus::async
163