blob: ed5237b5eac27f1d2317d749fdfb3a7b1fc05d40 [file] [log] [blame]
Patrick Williams290fa422022-08-29 09:15:58 -05001#include <sdbusplus/async/match.hpp>
2
3namespace sdbusplus::async
4{
5
William A. Kennington III9d8ba942023-07-13 15:13:51 -07006slot_t match::makeMatch(context& ctx, const std::string_view& pattern)
Patrick Williams290fa422022-08-29 09:15:58 -05007{
8 // C-style callback to redirect into this::handle_match.
Patrick Williamsd2149042023-05-10 07:50:13 -05009 static auto match_cb =
10 [](message::msgp_t msg, void* ctx, sd_bus_error*) noexcept {
Patrick Williams290fa422022-08-29 09:15:58 -050011 static_cast<match*>(ctx)->handle_match(message_t{msg});
12 return 0;
13 };
14
William A. Kennington III9d8ba942023-07-13 15:13:51 -070015 sd_bus_slot* s;
Patrick Williams290fa422022-08-29 09:15:58 -050016 auto r = sd_bus_add_match(get_busp(ctx.get_bus()), &s, pattern.data(),
17 match_cb, this);
18 if (r < 0)
19 {
20 throw exception::SdBusError(-r, "sd_bus_add_match (async::match)");
21 }
22
William A. Kennington III9d8ba942023-07-13 15:13:51 -070023 return slot_t{s, &sdbus_impl};
Patrick Williams290fa422022-08-29 09:15:58 -050024}
25
William A. Kennington III9d8ba942023-07-13 15:13:51 -070026match::match(context& ctx, const std::string_view& pattern) :
27 slot(makeMatch(ctx, pattern))
28{}
29
Patrick Williams1b7b54c2022-09-21 10:49:45 -050030match::~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
Patrick Williams290fa422022-08-29 09:15:58 -050045void 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
71void 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
80void 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