xref: /openbmc/sdbusplus/include/sdbusplus/async/callback.hpp (revision 36137e09614746b13603b5fbae79e6f70819c46b)
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