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 84 explicit match_completion(match& m) : m(m){}; 85 virtual ~match_completion() = default; 86 87 friend match; 88 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 { 106 match_operation(match& m, Receiver r) : 107 match_completion(m), receiver(std::move(r)) 108 {} 109 110 private: 111 void complete(message_t&& msg) noexcept override final 112 { 113 execution::set_value(std::move(receiver), std::move(msg)); 114 } 115 116 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; 130 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> 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 150 inline auto match::next() noexcept 151 { 152 return match_ns::match_sender(*this); 153 } 154 155 template <typename... Rs> 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