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 
tag_invoke(execution::start_t,sleep_operation & self)49     friend auto tag_invoke(execution::start_t, sleep_operation& self) noexcept
50     {
51         try
52         {
53             self.source = self.event_loop().add_oneshot_timer(handler, &self,
54                                                               self.time);
55         }
56         catch (...)
57         {
58             execution::set_error(std::move(self.receiver),
59                                  std::current_exception());
60         }
61     }
62 
63   private:
event_loopsdbusplus::async::timer_ns::sleep_operation64     event_t& event_loop()
65     {
66         return get_event_loop(ctx);
67     }
68 
69     event_t::time_resolution time;
70     event_source_t source;
71     R receiver;
72 };
73 
74 /** The delay Sender.
75  *
76  *  On connect, instantiates the completion event.
77  */
78 struct sleep_sender : public context_ref, details::context_friend
79 {
80     using is_sender = void;
81 
82     sleep_sender() = delete;
83 
sleep_sendersdbusplus::async::timer_ns::sleep_sender84     sleep_sender(context& ctx, event_t::time_resolution time) noexcept :
85         context_ref(ctx), time(time)
86     {}
87 
88     friend auto tag_invoke(execution::get_completion_signatures_t,
89                            const sleep_sender&, auto)
90         -> execution::completion_signatures<
91             execution::set_value_t(),
92             execution::set_error_t(std::exception_ptr),
93             execution::set_stopped_t()>;
94 
95     template <execution::receiver R>
tag_invoke(execution::connect_t,sleep_sender && self,R r)96     friend auto tag_invoke(execution::connect_t, sleep_sender&& self, R r)
97         -> sleep_operation<R>
98     {
99         return {self.ctx, self.time, std::move(r)};
100     }
101 
sleep_forsdbusplus::async::timer_ns::sleep_sender102     static auto sleep_for(context& ctx, event_t::time_resolution time)
103     {
104         // Run the delay sender and then switch back to the worker thread.
105         // The delay completion happens from the sd-event handler, which is
106         // ran on the 'caller' thread.
107         return execution::transfer(sleep_sender(ctx, time), get_scheduler(ctx));
108     }
109 
110   private:
111     event_t::time_resolution time;
112 };
113 
114 } // namespace timer_ns
115 
116 template <typename Rep, typename Period>
sleep_for(context & ctx,std::chrono::duration<Rep,Period> time)117 auto sleep_for(context& ctx, std::chrono::duration<Rep, Period> time)
118 {
119     return timer_ns::sleep_sender::sleep_for(
120         ctx, std::chrono::duration_cast<event_t::time_resolution>(time));
121 }
122 } // namespace sdbusplus::async
123