xref: /openbmc/sdbusplus/src/async/match.cpp (revision 6db88387)
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 = [](message::msgp_t msg, void* ctx,
10                               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 = sd_bus_add_match(get_busp(ctx), &s, pattern.data(), match_cb,
17                               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