110010b19SPatrick Williams #pragma once
210010b19SPatrick Williams
310010b19SPatrick Williams #include <systemd/sd-bus.h>
410010b19SPatrick Williams
510010b19SPatrick Williams #include <sdbusplus/async/execution.hpp>
610010b19SPatrick Williams #include <sdbusplus/message.hpp>
710010b19SPatrick Williams
810010b19SPatrick Williams #include <type_traits>
910010b19SPatrick Williams
1010010b19SPatrick Williams namespace sdbusplus::async
1110010b19SPatrick Williams {
1210010b19SPatrick Williams
1310010b19SPatrick Williams namespace callback_ns
1410010b19SPatrick Williams {
1510010b19SPatrick Williams
1610010b19SPatrick Williams template <typename Fn>
1710010b19SPatrick Williams concept takes_msg_handler =
1810010b19SPatrick Williams std::is_invocable_r_v<int, Fn, sd_bus_message_handler_t, void*>;
1910010b19SPatrick Williams
2010010b19SPatrick Williams template <takes_msg_handler Init>
2110010b19SPatrick Williams struct callback_sender;
2210010b19SPatrick Williams
2310010b19SPatrick Williams } // namespace callback_ns
2410010b19SPatrick Williams
2510010b19SPatrick Williams /** Create a sd_bus-callback Sender.
2610010b19SPatrick Williams *
2710010b19SPatrick Williams * In the sd_bus library there are many functions named `*_async` that take
2810010b19SPatrick Williams * a `sd_bus_message_handler_t` as the callback. This function turns them
2910010b19SPatrick Williams * into a Sender.
3010010b19SPatrick Williams *
3110010b19SPatrick Williams * The `Init` function is a simple indirect so that the library sd_bus
3210010b19SPatrick Williams * function can be called, but with the callback handler (and data) placed
3310010b19SPatrick Williams * in the right call positions.
3410010b19SPatrick Williams *
3510010b19SPatrick Williams * For example, `sd_bus_call_async` could be turned into a Sender with a
3610010b19SPatrick Williams * call to this and a small lambda such as:
3710010b19SPatrick Williams * ```
381ee60d6dSPatrick Williams * callback([bus = get_busp(ctx),
3910010b19SPatrick Williams * msg = std::move(msg)](auto cb, auto data) {
4010010b19SPatrick Williams * return sd_bus_call_async(bus, nullptr, msg.get(), cb, data, 0);
4110010b19SPatrick Williams * })
4210010b19SPatrick Williams * ```
4310010b19SPatrick Williams *
4410010b19SPatrick Williams * @param[in] i - A function which calls the underlying sd_bus library
4510010b19SPatrick Williams * function.
4610010b19SPatrick Williams *
4710010b19SPatrick Williams * @returns A Sender which completes when sd-bus calls the callback and yields
4810010b19SPatrick Williams * a `sdbusplus::message_t`.
4910010b19SPatrick Williams */
5010010b19SPatrick Williams template <callback_ns::takes_msg_handler Init>
callback(Init i)5110010b19SPatrick Williams auto callback(Init i)
5210010b19SPatrick Williams {
5310010b19SPatrick Williams return callback_ns::callback_sender<Init>(std::move(i));
5410010b19SPatrick Williams }
5510010b19SPatrick Williams
5610010b19SPatrick Williams namespace callback_ns
5710010b19SPatrick Williams {
5810010b19SPatrick Williams
5910010b19SPatrick Williams /** The operation which handles the Sender completion. */
6010010b19SPatrick Williams template <takes_msg_handler Init, execution::receiver R>
6110010b19SPatrick Williams struct callback_operation
6210010b19SPatrick Williams {
6310010b19SPatrick Williams callback_operation() = delete;
6410010b19SPatrick Williams callback_operation(callback_operation&&) = delete;
6510010b19SPatrick Williams
callback_operationsdbusplus::async::callback_ns::callback_operation6610010b19SPatrick Williams callback_operation(Init&& init, R&& r) :
6710010b19SPatrick Williams init(std::move(init)), receiver(std::move(r))
6810010b19SPatrick Williams {}
6910010b19SPatrick Williams
7010010b19SPatrick Williams // Handle the call from sd-bus by ensuring there were no errors
7110010b19SPatrick Williams // and setting the completion value to the resulting message.
handlersdbusplus::async::callback_ns::callback_operation7210010b19SPatrick Williams static int handler(sd_bus_message* m, void* cb, sd_bus_error* e) noexcept
7310010b19SPatrick Williams {
7410010b19SPatrick Williams callback_operation& self = *static_cast<callback_operation*>(cb);
7510010b19SPatrick Williams try
7610010b19SPatrick Williams {
7710010b19SPatrick Williams // Check 'e' for error.
7810010b19SPatrick Williams if ((nullptr != e) && (sd_bus_error_is_set(e)))
7910010b19SPatrick Williams {
8010010b19SPatrick Williams throw exception::SdBusError(e, "callback");
8110010b19SPatrick Williams }
8210010b19SPatrick Williams
8310010b19SPatrick Williams message_t msg{m};
8410010b19SPatrick Williams
8510010b19SPatrick Williams // Check the message response for error.
8610010b19SPatrick Williams if (msg.is_method_error())
8710010b19SPatrick Williams {
8810010b19SPatrick Williams auto err = *msg.get_error();
8910010b19SPatrick Williams throw exception::SdBusError(&err, "method");
9010010b19SPatrick Williams }
9110010b19SPatrick Williams
9210010b19SPatrick Williams execution::set_value(std::move(self.receiver), std::move(msg));
9310010b19SPatrick Williams }
9410010b19SPatrick Williams catch (...)
9510010b19SPatrick Williams {
9610010b19SPatrick Williams execution::set_error(std::move(self.receiver),
9710010b19SPatrick Williams std::current_exception());
9810010b19SPatrick Williams }
9910010b19SPatrick Williams return 0;
10010010b19SPatrick Williams }
10110010b19SPatrick Williams
10210010b19SPatrick Williams // Call the init function upon Sender start.
tag_invoke(execution::start_t,callback_operation & self)10310010b19SPatrick Williams friend void tag_invoke(execution::start_t,
10410010b19SPatrick Williams callback_operation& self) noexcept
10510010b19SPatrick Williams {
10610010b19SPatrick Williams try
10710010b19SPatrick Williams {
10810010b19SPatrick Williams auto rc = self.init(handler, &self);
10910010b19SPatrick Williams if (rc < 0)
11010010b19SPatrick Williams {
11110010b19SPatrick Williams throw exception::SdBusError(-rc, __PRETTY_FUNCTION__);
11210010b19SPatrick Williams }
11310010b19SPatrick Williams }
11410010b19SPatrick Williams catch (...)
11510010b19SPatrick Williams {
11610010b19SPatrick Williams execution::set_error(std::move(self.receiver),
11710010b19SPatrick Williams std::current_exception());
11810010b19SPatrick Williams }
11910010b19SPatrick Williams }
12010010b19SPatrick Williams
12110010b19SPatrick Williams private:
12210010b19SPatrick Williams Init init;
12310010b19SPatrick Williams R receiver;
12410010b19SPatrick Williams };
12510010b19SPatrick Williams
12610010b19SPatrick Williams /** The Sender for a callback.
12710010b19SPatrick Williams *
12810010b19SPatrick Williams * The basically just holds the Init function until the Sender is connected
12910010b19SPatrick Williams * to (co_awaited on for co-routines), when it is turned into a pending
13010010b19SPatrick Williams * operation.
13110010b19SPatrick Williams */
13210010b19SPatrick Williams template <takes_msg_handler Init>
13310010b19SPatrick Williams struct callback_sender
13410010b19SPatrick Williams {
1359c6ec9b3SPatrick Williams using is_sender = void;
1369c6ec9b3SPatrick Williams
callback_sendersdbusplus::async::callback_ns::callback_sender13710010b19SPatrick Williams explicit callback_sender(Init init) : init(std::move(init)) {};
13810010b19SPatrick Williams
13910010b19SPatrick Williams // This Sender yields a message_t.
14010010b19SPatrick Williams friend auto tag_invoke(execution::get_completion_signatures_t,
14110010b19SPatrick Williams const callback_sender&, auto)
1426ba44310SPatrick Williams -> execution::completion_signatures<execution::set_value_t(message_t),
1436ba44310SPatrick Williams execution::set_stopped_t()>;
14410010b19SPatrick Williams
14510010b19SPatrick Williams template <execution::receiver R>
tag_invoke(execution::connect_t,callback_sender && self,R r)146*36137e09SPatrick Williams friend auto tag_invoke(execution::connect_t, callback_sender&& self, R r)
147*36137e09SPatrick Williams -> callback_operation<Init, R>
14810010b19SPatrick Williams {
14910010b19SPatrick Williams return {std::move(self.init), std::move(r)};
15010010b19SPatrick Williams }
15110010b19SPatrick Williams
15210010b19SPatrick Williams private:
15310010b19SPatrick Williams Init init;
15410010b19SPatrick Williams };
15510010b19SPatrick Williams
15610010b19SPatrick Williams } // namespace callback_ns
15710010b19SPatrick Williams
15810010b19SPatrick Williams } // namespace sdbusplus::async
159