1 #pragma once 2 3 #include <systemd/sd-event.h> 4 5 #include <chrono> 6 #include <mutex> 7 #include <utility> 8 9 namespace sdbusplus 10 { 11 namespace event 12 { 13 class event; 14 15 /** RAII holder for sd_event_sources */ 16 class source 17 { 18 public: 19 friend event; 20 21 source() = default; source(event & e)22 explicit source(event& e) : ev(&e) {} 23 24 source(const source&) = delete; 25 source(source&&); 26 source& operator=(const source&) = delete; 27 source& operator=(source&&); 28 ~source(); 29 30 private: source(event & e,sd_event_source * & s)31 source(event& e, sd_event_source*& s) : ev(&e) 32 { 33 sourcep = std::exchange(s, nullptr); 34 } 35 36 event* ev = nullptr; 37 sd_event_source* sourcep = nullptr; 38 }; 39 40 /** sd-event wrapper for eventfd 41 * 42 * This can be used to create something similar to a std::condition_variable 43 * but backed by sd-event. 44 */ 45 class condition 46 { 47 public: 48 friend event; 49 50 condition() = delete; condition(event & e)51 explicit condition(event& e) : condition_source(e) {} 52 condition(const condition&) = delete; 53 condition(condition&&); 54 55 condition& operator=(const condition&) = delete; 56 condition& operator=(condition&&); 57 ~condition()58 ~condition() 59 { 60 if (fd >= 0) 61 { 62 close(fd); 63 } 64 } 65 66 /** Increment the signal count on the eventfd. */ 67 void signal(); 68 /** Acknowledge all pending signals on the eventfd. */ 69 void ack(); 70 71 private: condition(source && s,int && f)72 condition(source&& s, int&& f) : 73 condition_source(std::move(s)), fd(std::exchange(f, -1)) 74 {} 75 76 source condition_source; 77 int fd = -1; 78 }; 79 80 /** sd-event based run-loop implementation. 81 * 82 * This is sd-event is thread-safe in the sense that one thread may be 83 * executing 'run_one' while other threads create (or destruct) additional 84 * sources. This might result in the 'run_one' exiting having done no 85 * work, but the state of the underlying sd-event structures is kept 86 * thread-safe. 87 */ 88 class event 89 { 90 public: 91 using time_resolution = std::chrono::microseconds; 92 93 event(); 94 event(const event&) = delete; 95 event(event&& e) = delete; 96 ~event()97 ~event() 98 { 99 sd_event_unref(eventp); 100 } 101 102 /** Execute a single iteration of the run-loop (see sd_event_run). */ 103 void run_one(time_resolution timeout = time_resolution::max()); 104 /** Force a pending `run_one` to exit. */ 105 void break_run(); 106 107 /** Add a file-descriptor source to the sd-event (see sd_event_add_io). */ 108 source add_io(int fd, uint32_t events, sd_event_io_handler_t handler, 109 void* data); 110 111 /** Add a eventfd-based sdbusplus::event::condition to the run-loop. */ 112 condition add_condition(sd_event_io_handler_t handler, void* data); 113 114 /** Add a one shot timer source to the run-loop. */ 115 source add_oneshot_timer( 116 sd_event_time_handler_t handler, void* data, time_resolution time, 117 time_resolution accuracy = std::chrono::milliseconds(1)); 118 119 friend source; 120 121 private: 122 static int run_wakeup(sd_event_source*, int, uint32_t, void*); 123 124 sd_event* eventp = nullptr; 125 126 // Condition to allow 'break_run' to exit the run-loop. 127 condition run_condition{*this}; 128 129 // Lock for the sd_event. 130 // 131 // There are cases where we need to lock the mutex from inside the context 132 // of a sd-event callback, while the lock is already held. Use a 133 // recursive_mutex to allow this. 134 std::recursive_mutex lock{}; 135 136 // Safely get the lock, possibly signaling the running 'run_one' to exit. 137 template <bool Signal = true> 138 std::unique_lock<std::recursive_mutex> obtain_lock(); 139 // When obtain_lock signals 'run_one' to exit, we want a priority of 140 // obtaining the lock so that the 'run_one' task doesn't run and reclaim 141 // the lock before the signaller can run. This stage is first obtained 142 // prior to getting the primary lock in order to set an order. 143 std::mutex obtain_lock_stage{}; 144 }; 145 146 } // namespace event 147 148 using event_t = event::event; 149 using event_source_t = event::source; 150 using event_cond_t = event::condition; 151 152 } // namespace sdbusplus 153