1 #include <sdbusplus/async/match.hpp> 2 3 namespace sdbusplus::async 4 { 5 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 = 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 26 match::match(context& ctx, const std::string_view& pattern) : 27 slot(makeMatch(ctx, pattern)) 28 {} 29 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 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 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 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