1 #pragma once 2 3 #include <sdbusplus/async/execution.hpp> 4 #include <sdbusplus/async/task.hpp> 5 #include <sdbusplus/bus.hpp> 6 #include <sdbusplus/event.hpp> 7 8 #include <condition_variable> 9 #include <mutex> 10 #include <stop_token> 11 #include <thread> 12 13 namespace sdbusplus::async 14 { 15 16 namespace details 17 { 18 struct wait_process_completion; 19 struct context_friend; 20 } // namespace details 21 22 /** @brief A run-loop context for handling asynchronous dbus operations. 23 * 24 * This class encapsulates the run-loop for asynchronous operations, 25 * especially those using co-routines. Primarily, the object is given 26 * co-routines (or Senders) for the processing, via `spawn`, and then the 27 * object is `run` which handles all asynchronous operations until the 28 * context is stopped, via `request_stop`. 29 * 30 * The context has two threads: 31 * - The thread which called `run`, often from `main`, and named the 32 * `caller`. 33 * - A worker thread (`worker`), which performs the async operations. 34 * 35 * In order to avoid blocking the worker needlessly, the `caller` is the only 36 * thread which called `sd_bus_wait`, but the `worker` is where all 37 * `sd_bus_process` calls are performed. There is a condition-variable based 38 * handshake between the two threads to accomplish this interaction. 39 */ 40 class context : public bus::details::bus_friend 41 { 42 public: 43 explicit context(bus_t&& bus = bus::new_default()); 44 context(context&&) = delete; 45 context(const context&) = delete; 46 47 // The context destructor can throw because it will throw (and likely 48 // terminate) if it has been improperly shutdown in order to avoid leaking 49 // work. 50 ~context() noexcept(false); 51 52 /** Run the loop. */ 53 void run(); 54 55 /** Spawn a Sender to run on the context. 56 * 57 * @param[in] sender - The Sender to run. 58 */ 59 template <execution::sender_of<execution::set_value_t()> Snd> 60 void spawn(Snd&& sender) 61 { 62 check_stop_requested(); 63 64 pending_tasks.spawn( 65 std::move(execution::on(loop.get_scheduler(), std::move(sender)))); 66 67 spawn_watcher(); 68 } 69 70 bus_t& get_bus() noexcept 71 { 72 return bus; 73 } 74 operator bus_t&() noexcept 75 { 76 return bus; 77 } 78 79 bool request_stop() noexcept 80 { 81 return initial_stop.request_stop(); 82 } 83 bool stop_requested() noexcept 84 { 85 return initial_stop.stop_requested(); 86 } 87 88 friend details::wait_process_completion; 89 friend details::context_friend; 90 91 private: 92 bus_t bus; 93 event_source_t dbus_source; 94 event_t event_loop{}; 95 96 /** The async run-loop from std::execution. */ 97 execution::run_loop loop{}; 98 /** The worker thread to handle async tasks. */ 99 std::thread worker_thread{}; 100 /** Stop source */ 101 std::stop_source initial_stop{}; 102 103 async_scope pending_tasks{}; 104 // In order to coordinate final completion of work, we keep some tasks 105 // on a separate scope (the ones which maintain the sd-event/dbus state 106 // and keep a final stop-source for them. 107 async_scope internal_tasks{}; 108 std::stop_source final_stop{}; 109 110 // Lock and condition variable to signal `caller`. 111 std::mutex lock{}; 112 std::condition_variable caller_wait{}; 113 114 bool spawn_watcher_running = false; 115 116 /** Completion object to signal the worker that 'sd_bus_wait' is done. */ 117 details::wait_process_completion* staged = nullptr; 118 details::wait_process_completion* pending = nullptr; 119 bool wait_process_stopped = false; 120 121 void worker_run(); 122 void spawn_complete(); 123 void check_stop_requested(); 124 void spawn_watcher(); 125 126 void caller_run(); 127 void wait_for_wait_process_stopped(); 128 129 static int dbus_event_handle(sd_event_source*, int, uint32_t, void*); 130 }; 131 132 /** @brief Simple holder of a context& 133 * 134 * A common pattern is for classes to hold a reference to a 135 * context. Add a class that can be inherited instead of 136 * having as a class member. This allows the context-ref constructor to be 137 * placed earliest in the ctor initializer list, so that the reference can be 138 * available from inherited classes (ex. for CRTP patterns). 139 * 140 */ 141 class context_ref 142 { 143 public: 144 context_ref() = delete; 145 explicit context_ref(context& ctx) : ctx(ctx) {} 146 147 protected: 148 context& ctx; 149 }; 150 151 namespace details 152 { 153 struct context_friend 154 { 155 static event_t& get_event_loop(context& ctx) 156 { 157 return ctx.event_loop; 158 } 159 160 static auto get_scheduler(context& ctx) 161 { 162 return ctx.loop.get_scheduler(); 163 } 164 }; 165 } // namespace details 166 167 } // namespace sdbusplus::async 168