xref: /openbmc/sdbusplus/include/sdbusplus/async/timer.hpp (revision 5e36f4ab9b19cebc8a1e9be82ae0c3363838b4ce)
1 #pragma once
2 
3 #include <sdbusplus/async/context.hpp>
4 #include <sdbusplus/async/execution.hpp>
5 #include <sdbusplus/event.hpp>
6 
7 namespace sdbusplus::async
8 {
9 
10 /** sleep_for Sender
11  *
12  *  This Sender performs the equivalent of `std::this_thread::sleep_for`,
13  *  in an async context.
14  *
15  *  @param[in] ctx The async context.
16  *  @param[in] time The length of time to delay.
17  *
18  *  @return A sender which completes after time.
19  */
20 template <typename Rep, typename Period>
21 auto sleep_for(context& ctx, std::chrono::duration<Rep, Period> time);
22 
23 namespace timer_ns
24 {
25 
26 /* The sleep completion event.
27  *
28  * On start, creates the sd-event timer.
29  * On callback, completes the Receiver.
30  */
31 template <execution::receiver R>
32 struct sleep_operation : public context_ref, details::context_friend
33 {
34     sleep_operation() = delete;
35     sleep_operation(sleep_operation&&) = delete;
36 
sleep_operationsdbusplus::async::timer_ns::sleep_operation37     sleep_operation(context& ctx, event_t::time_resolution time, R&& r) :
38         context_ref(ctx), time(time), receiver(std::move(r))
39     {}
40 
handlersdbusplus::async::timer_ns::sleep_operation41     static int handler(sd_event_source*, uint64_t, void* data) noexcept
42     {
43         auto self = static_cast<sleep_operation<R>*>(data);
44         execution::set_value(std::move(self->receiver));
45 
46         return 0;
47     }
48 
startsdbusplus::async::timer_ns::sleep_operation49     void start() noexcept
50     {
51         try
52         {
53             source = event_loop().add_oneshot_timer(handler, this, time);
54         }
55         catch (...)
56         {
57             execution::set_error(std::move(receiver), std::current_exception());
58         }
59     }
60 
61   private:
event_loopsdbusplus::async::timer_ns::sleep_operation62     event_t& event_loop()
63     {
64         return get_event_loop(ctx);
65     }
66 
67     event_t::time_resolution time;
68     event_source_t source;
69     R receiver;
70 };
71 
72 /** The delay Sender.
73  *
74  *  On connect, instantiates the completion event.
75  */
76 struct sleep_sender : public context_ref, details::context_friend
77 {
78     using sender_concept = execution::sender_t;
79 
80     sleep_sender() = delete;
81 
sleep_sendersdbusplus::async::timer_ns::sleep_sender82     sleep_sender(context& ctx, event_t::time_resolution time) noexcept :
83         context_ref(ctx), time(time)
84     {}
85 
86     template <typename Self, class... Env>
87     static constexpr auto get_completion_signatures(Self&&, Env&&...)
88         -> execution::completion_signatures<
89             execution::set_value_t(),
90             execution::set_error_t(std::exception_ptr),
91             execution::set_stopped_t()>;
92 
93     template <execution::receiver R>
connectsdbusplus::async::timer_ns::sleep_sender94     auto connect(R r) -> sleep_operation<R>
95     {
96         return {ctx, time, std::move(r)};
97     }
98 
sleep_forsdbusplus::async::timer_ns::sleep_sender99     static auto sleep_for(context& ctx, event_t::time_resolution time)
100     {
101         // Run the delay sender and then switch back to the worker thread.
102         // The delay completion happens from the sd-event handler, which is
103         // ran on the 'caller' thread.
104         return execution::continues_on(sleep_sender(ctx, time),
105                                        get_scheduler(ctx));
106     }
107 
108   private:
109     event_t::time_resolution time;
110 };
111 
112 } // namespace timer_ns
113 
114 template <typename Rep, typename Period>
sleep_for(context & ctx,std::chrono::duration<Rep,Period> time)115 auto sleep_for(context& ctx, std::chrono::duration<Rep, Period> time)
116 {
117     return timer_ns::sleep_sender::sleep_for(
118         ctx, std::chrono::duration_cast<event_t::time_resolution>(time));
119 }
120 } // namespace sdbusplus::async
121