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 37 sleep_operation(context& ctx, event_t::time_resolution time, R&& r) : 38 context_ref(ctx), time(time), receiver(std::move(r)) 39 {} 40 41 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 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: 64 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 84 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> 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 102 static task<> 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 co_await sleep_sender(ctx, time); 108 co_await execution::schedule(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> 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