1 #include <sdbusplus/async/match.hpp>
2
3 namespace sdbusplus::async
4 {
5
makeMatch(context & ctx,const std::string_view & pattern)6 slot_t match::makeMatch(context& ctx, const std::string_view& pattern)
7 {
8 // C-style callback to redirect into this::handle_match.
9 static auto match_cb =
10 [](message::msgp_t msg, void* ctx, sd_bus_error*) noexcept {
11 static_cast<match*>(ctx)->handle_match(message_t{msg});
12 return 0;
13 };
14
15 sd_bus_slot* s;
16 auto r =
17 sd_bus_add_match(get_busp(ctx), &s, pattern.data(), match_cb, this);
18 if (r < 0)
19 {
20 throw exception::SdBusError(-r, "sd_bus_add_match (async::match)");
21 }
22
23 return slot_t{s, &sdbus_impl};
24 }
25
match(context & ctx,const std::string_view & pattern)26 match::match(context& ctx, const std::string_view& pattern) :
27 slot(makeMatch(ctx, pattern))
28 {}
29
~match()30 match::~match()
31 {
32 match_ns::match_completion* c = nullptr;
33
34 {
35 std::lock_guard l{lock};
36 c = std::exchange(complete, nullptr);
37 }
38
39 if (c)
40 {
41 c->stop();
42 }
43 }
44
arm()45 void match_ns::match_completion::arm() noexcept
46 {
47 // Set ourselves as the awaiting Receiver and see if there is a message
48 // to immediately complete on.
49
50 std::unique_lock lock{m.lock};
51
52 if (std::exchange(m.complete, this) != nullptr)
53 {
54 // We do not support two awaiters; throw exception. Since we are in
55 // a noexcept context this will std::terminate anyhow, which is
56 // approximately the same as 'assert' but with better information.
57 try
58 {
59 throw std::logic_error(
60 "match_completion started with another await already pending!");
61 }
62 catch (...)
63 {
64 std::terminate();
65 }
66 }
67
68 m.handle_completion(std::move(lock));
69 }
70
handle_match(message_t && msg)71 void match::handle_match(message_t&& msg) noexcept
72 {
73 // Insert the message into the queue and see if there is a pair ready for
74 // completion (Receiver + message).
75 std::unique_lock l{lock};
76 queue.emplace(std::move(msg));
77 handle_completion(std::move(l));
78 }
79
handle_completion(std::unique_lock<std::mutex> && l)80 void match::handle_completion(std::unique_lock<std::mutex>&& l) noexcept
81 {
82 auto lock = std::move(l);
83
84 // If there is no match_completion, there is no awaiting Receiver.
85 // If the queue is empty, there is no message waiting, so the waiting
86 // Receiver isn't complete.
87 if ((complete == nullptr) || queue.empty())
88 {
89 return;
90 }
91
92 // Get the waiting completion and message.
93 auto c = std::exchange(complete, nullptr);
94 auto msg = std::move(queue.front());
95 queue.pop();
96
97 // Unlock before calling complete because the completed task may run and
98 // attempt to complete on the next event (and thus deadlock).
99 lock.unlock();
100
101 // Signal completion.
102 c->complete(std::move(msg));
103 }
104
105 } // namespace sdbusplus::async
106