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 =
54 self.event_loop().add_oneshot_timer(handler, &self, 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::continues_on(sleep_sender(ctx, time),
108 get_scheduler(ctx));
109 }
110
111 private:
112 event_t::time_resolution time;
113 };
114
115 } // namespace timer_ns
116
117 template <typename Rep, typename Period>
sleep_for(context & ctx,std::chrono::duration<Rep,Period> time)118 auto sleep_for(context& ctx, std::chrono::duration<Rep, Period> time)
119 {
120 return timer_ns::sleep_sender::sleep_for(
121 ctx, std::chrono::duration_cast<event_t::time_resolution>(time));
122 }
123 } // namespace sdbusplus::async
124