blob: 951db43903e6ea48349aa0b49a205ce77b915c74 [file] [log] [blame]
Patrick Williamsd2b00442022-09-16 08:28:07 -05001#pragma once
2
3#include <systemd/sd-event.h>
4
5#include <chrono>
6#include <mutex>
7#include <utility>
8
9namespace sdbusplus
10{
11namespace event
12{
13class event;
14
15/** RAII holder for sd_event_sources */
16class source
17{
18 public:
19 friend event;
20
21 source() = default;
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:
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 */
45class condition
46{
47 public:
48 friend event;
49
50 condition() = delete;
Ed Tanousb8be5992023-01-04 15:56:20 -080051 explicit condition(event& e) : condition_source(e) {}
Patrick Williamsd2b00442022-09-16 08:28:07 -050052 condition(const condition&) = delete;
53 condition(condition&&);
54
55 condition& operator=(const condition&) = delete;
56 condition& operator=(condition&&);
57
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:
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 */
88class event
89{
90 public:
Patrick Williams435eb1b2022-09-16 16:22:07 -050091 using time_resolution = std::chrono::microseconds;
92
Patrick Williamsd2b00442022-09-16 08:28:07 -050093 event();
94 event(const event&) = delete;
95 event(event&& e) = delete;
96
97 ~event()
98 {
99 sd_event_unref(eventp);
100 }
101
102 /** Execute a single iteration of the run-loop (see sd_event_run). */
Patrick Williams435eb1b2022-09-16 16:22:07 -0500103 void run_one(time_resolution timeout = time_resolution::max());
Patrick Williamsd2b00442022-09-16 08:28:07 -0500104 /** 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
Patrick Williams3ce31592022-09-16 14:05:56 -0500114 /** Add a one shot timer source to the run-loop. */
115 source add_oneshot_timer(
Patrick Williams435eb1b2022-09-16 16:22:07 -0500116 sd_event_time_handler_t handler, void* data, time_resolution time,
117 time_resolution accuracy = std::chrono::milliseconds(1));
Patrick Williams3ce31592022-09-16 14:05:56 -0500118
Patrick Williamsd2b00442022-09-16 08:28:07 -0500119 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();
Patrick Williams5c50fc62023-04-05 12:27:03 -0500139 // 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{};
Patrick Williamsd2b00442022-09-16 08:28:07 -0500144};
145
146} // namespace event
147
148using event_t = event::event;
149using event_source_t = event::source;
150using event_cond_t = event::condition;
151
152} // namespace sdbusplus