xref: /openbmc/sdbusplus/src/async/match.cpp (revision 06f265f6f18e22b1fb68761edb50ecd6722c2a47)
1290fa427SPatrick Williams #include <sdbusplus/async/match.hpp>
2290fa427SPatrick Williams 
3290fa427SPatrick Williams namespace sdbusplus::async
4290fa427SPatrick Williams {
5290fa427SPatrick Williams 
makeMatch(context & ctx,const std::string_view & pattern)69d8ba947SWilliam A. Kennington III slot_t match::makeMatch(context& ctx, const std::string_view& pattern)
7290fa427SPatrick Williams {
8290fa427SPatrick Williams     // C-style callback to redirect into this::handle_match.
9*06f265f6SPatrick Williams     static auto match_cb =
10*06f265f6SPatrick Williams         [](message::msgp_t msg, void* ctx, sd_bus_error*) noexcept {
11290fa427SPatrick Williams             static_cast<match*>(ctx)->handle_match(message_t{msg});
12290fa427SPatrick Williams             return 0;
13290fa427SPatrick Williams         };
14290fa427SPatrick Williams 
159d8ba947SWilliam A. Kennington III     sd_bus_slot* s;
16*06f265f6SPatrick Williams     auto r =
17*06f265f6SPatrick Williams         sd_bus_add_match(get_busp(ctx), &s, pattern.data(), match_cb, this);
18290fa427SPatrick Williams     if (r < 0)
19290fa427SPatrick Williams     {
20290fa427SPatrick Williams         throw exception::SdBusError(-r, "sd_bus_add_match (async::match)");
21290fa427SPatrick Williams     }
22290fa427SPatrick Williams 
239d8ba947SWilliam A. Kennington III     return slot_t{s, &sdbus_impl};
24290fa427SPatrick Williams }
25290fa427SPatrick Williams 
match(context & ctx,const std::string_view & pattern)269d8ba947SWilliam A. Kennington III match::match(context& ctx, const std::string_view& pattern) :
279d8ba947SWilliam A. Kennington III     slot(makeMatch(ctx, pattern))
289d8ba947SWilliam A. Kennington III {}
299d8ba947SWilliam A. Kennington III 
~match()301b7b54caSPatrick Williams match::~match()
311b7b54caSPatrick Williams {
321b7b54caSPatrick Williams     match_ns::match_completion* c = nullptr;
331b7b54caSPatrick Williams 
341b7b54caSPatrick Williams     {
351b7b54caSPatrick Williams         std::lock_guard l{lock};
361b7b54caSPatrick Williams         c = std::exchange(complete, nullptr);
371b7b54caSPatrick Williams     }
381b7b54caSPatrick Williams 
391b7b54caSPatrick Williams     if (c)
401b7b54caSPatrick Williams     {
411b7b54caSPatrick Williams         c->stop();
421b7b54caSPatrick Williams     }
431b7b54caSPatrick Williams }
441b7b54caSPatrick Williams 
arm()45290fa427SPatrick Williams void match_ns::match_completion::arm() noexcept
46290fa427SPatrick Williams {
47290fa427SPatrick Williams     // Set ourselves as the awaiting Receiver and see if there is a message
48290fa427SPatrick Williams     // to immediately complete on.
49290fa427SPatrick Williams 
50290fa427SPatrick Williams     std::unique_lock lock{m.lock};
51290fa427SPatrick Williams 
52290fa427SPatrick Williams     if (std::exchange(m.complete, this) != nullptr)
53290fa427SPatrick Williams     {
54290fa427SPatrick Williams         // We do not support two awaiters; throw exception.  Since we are in
55290fa427SPatrick Williams         // a noexcept context this will std::terminate anyhow, which is
56290fa427SPatrick Williams         // approximately the same as 'assert' but with better information.
57290fa427SPatrick Williams         try
58290fa427SPatrick Williams         {
59290fa427SPatrick Williams             throw std::logic_error(
60290fa427SPatrick Williams                 "match_completion started with another await already pending!");
61290fa427SPatrick Williams         }
62290fa427SPatrick Williams         catch (...)
63290fa427SPatrick Williams         {
64290fa427SPatrick Williams             std::terminate();
65290fa427SPatrick Williams         }
66290fa427SPatrick Williams     }
67290fa427SPatrick Williams 
68290fa427SPatrick Williams     m.handle_completion(std::move(lock));
69290fa427SPatrick Williams }
70290fa427SPatrick Williams 
handle_match(message_t && msg)71290fa427SPatrick Williams void match::handle_match(message_t&& msg) noexcept
72290fa427SPatrick Williams {
73290fa427SPatrick Williams     // Insert the message into the queue and see if there is a pair ready for
74290fa427SPatrick Williams     // completion (Receiver + message).
75290fa427SPatrick Williams     std::unique_lock l{lock};
76290fa427SPatrick Williams     queue.emplace(std::move(msg));
77290fa427SPatrick Williams     handle_completion(std::move(l));
78290fa427SPatrick Williams }
79290fa427SPatrick Williams 
handle_completion(std::unique_lock<std::mutex> && l)80290fa427SPatrick Williams void match::handle_completion(std::unique_lock<std::mutex>&& l) noexcept
81290fa427SPatrick Williams {
82290fa427SPatrick Williams     auto lock = std::move(l);
83290fa427SPatrick Williams 
84290fa427SPatrick Williams     // If there is no match_completion, there is no awaiting Receiver.
85290fa427SPatrick Williams     // If the queue is empty, there is no message waiting, so the waiting
86290fa427SPatrick Williams     // Receiver isn't complete.
87290fa427SPatrick Williams     if ((complete == nullptr) || queue.empty())
88290fa427SPatrick Williams     {
89290fa427SPatrick Williams         return;
90290fa427SPatrick Williams     }
91290fa427SPatrick Williams 
92290fa427SPatrick Williams     // Get the waiting completion and message.
93290fa427SPatrick Williams     auto c = std::exchange(complete, nullptr);
94290fa427SPatrick Williams     auto msg = std::move(queue.front());
95290fa427SPatrick Williams     queue.pop();
96290fa427SPatrick Williams 
97290fa427SPatrick Williams     // Unlock before calling complete because the completed task may run and
98290fa427SPatrick Williams     // attempt to complete on the next event (and thus deadlock).
99290fa427SPatrick Williams     lock.unlock();
100290fa427SPatrick Williams 
101290fa427SPatrick Williams     // Signal completion.
102290fa427SPatrick Williams     c->complete(std::move(msg));
103290fa427SPatrick Williams }
104290fa427SPatrick Williams 
105290fa427SPatrick Williams } // namespace sdbusplus::async
106