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 74 namespace match_ns 75 { 76 // Virtual class to handle the match Receiver completions. 77 struct match_completion 78 { 79 match_completion() = delete; 80 match_completion(match_completion&&) = delete; 81 82 explicit match_completion(match& m) : m(m){}; 83 virtual ~match_completion() = default; 84 85 friend match; 86 87 friend void tag_invoke(execution::start_t, match_completion& self) noexcept 88 { 89 self.arm(); 90 } 91 92 private: 93 virtual void complete(message_t&&) noexcept = 0; 94 virtual void stop() noexcept = 0; 95 void arm() noexcept; 96 97 match& m; 98 }; 99 100 // Implementation (templated based on Receiver) of match_completion. 101 template <execution::receiver Receiver> 102 struct match_operation : match_completion 103 { 104 match_operation(match& m, Receiver r) : 105 match_completion(m), receiver(std::move(r)) 106 {} 107 108 private: 109 void complete(message_t&& msg) noexcept override final 110 { 111 execution::set_value(std::move(receiver), std::move(msg)); 112 } 113 114 void stop() noexcept override final 115 { 116 execution::set_stopped(std::move(receiver)); 117 } 118 119 Receiver receiver; 120 }; 121 122 // match Sender implementation. 123 struct match_sender 124 { 125 match_sender() = delete; 126 explicit match_sender(match& m) noexcept : m(m){}; 127 128 friend auto tag_invoke(execution::get_completion_signatures_t, 129 const match_sender&, auto) 130 -> execution::completion_signatures<execution::set_value_t(message_t)>; 131 132 template <execution::receiver R> 133 friend auto tag_invoke(execution::connect_t, match_sender&& self, R r) 134 -> match_operation<R> 135 { 136 return {self.m, std::move(r)}; 137 } 138 139 private: 140 match& m; 141 }; 142 143 }; // namespace match_ns 144 145 auto match::next() noexcept 146 { 147 return match_ns::match_sender(*this); 148 } 149 150 template <typename... Rs> 151 auto match::next() noexcept 152 { 153 return match_ns::match_sender(*this) | 154 execution::then([](message_t&& m) { return m.unpack<Rs...>(); }); 155 } 156 157 } // namespace sdbusplus::async 158