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 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