blob: 132db0863d755d48ef7eb0e3a671e73ce3bebb8b [file] [log] [blame]
#pragma once
#include <sdbusplus/async/context.hpp>
#include <sdbusplus/async/execution.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/message.hpp>
#include <sdbusplus/slot.hpp>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <string_view>
namespace sdbusplus::async
{
namespace match_ns
{
struct match_completion;
}
/** Generator of dbus match Senders.
*
* This class registers a signal match pattern with the dbus and generates
* Senders using `next` to await the next matching signal.
*/
class match : private bus::details::bus_friend
{
public:
match() = delete;
match(const match&) = delete;
match& operator=(const match&) = delete;
match(match&&) = delete;
match& operator=(match&&) = delete;
~match();
/** Construct the match using the `pattern` string on the bus managed by the
* context. */
match(context& ctx, const std::string_view& pattern);
/** Get the Sender for the next event (as message).
*
* Note: the implementation only supports a single awaiting task. Two
* tasks should not share this object and both call `next`.
*/
auto next() noexcept;
/** Get the Sender for the next event, which yields one of:
* void, Rs, tuple<Rs...>
*
* Note: the implementation only supports a single awaiting task. Two
* tasks should not share this object and both call `next`.
*/
template <typename... Rs>
auto next() noexcept;
friend match_ns::match_completion;
private:
sdbusplus::slot_t slot{};
std::mutex lock{};
std::queue<sdbusplus::message_t> queue{};
match_ns::match_completion* complete = nullptr;
/** Handle an incoming match event. */
void handle_match(message_t&&) noexcept;
/** Signal completion if there is an awaiting Receiver.
*
* This must be called with `lock` held (and ownership transfers).
*/
void handle_completion(std::unique_lock<std::mutex>&&) noexcept;
};
namespace match_ns
{
// Virtual class to handle the match Receiver completions.
struct match_completion
{
match_completion() = delete;
match_completion(match_completion&&) = delete;
explicit match_completion(match& m) : m(m){};
virtual ~match_completion() = default;
friend match;
friend void tag_invoke(execution::start_t, match_completion& self) noexcept
{
self.arm();
}
private:
virtual void complete(message_t&&) noexcept = 0;
virtual void stop() noexcept = 0;
void arm() noexcept;
match& m;
};
// Implementation (templated based on Receiver) of match_completion.
template <execution::receiver Receiver>
struct match_operation : match_completion
{
match_operation(match& m, Receiver r) :
match_completion(m), receiver(std::move(r))
{}
private:
void complete(message_t&& msg) noexcept override final
{
execution::set_value(std::move(receiver), std::move(msg));
}
void stop() noexcept override final
{
execution::set_stopped(std::move(receiver));
}
Receiver receiver;
};
// match Sender implementation.
struct match_sender
{
match_sender() = delete;
explicit match_sender(match& m) noexcept : m(m){};
friend auto tag_invoke(execution::get_completion_signatures_t,
const match_sender&, auto)
-> execution::completion_signatures<execution::set_value_t(message_t)>;
template <execution::receiver R>
friend auto tag_invoke(execution::connect_t, match_sender&& self, R r)
-> match_operation<R>
{
return {self.m, std::move(r)};
}
private:
match& m;
};
}; // namespace match_ns
auto match::next() noexcept
{
return match_ns::match_sender(*this);
}
template <typename... Rs>
auto match::next() noexcept
{
return match_ns::match_sender(*this) |
execution::then([](message_t&& m) { return m.unpack<Rs...>(); });
}
} // namespace sdbusplus::async