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;
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  */
45 class condition
46 {
47   public:
48     friend event;
49 
50     condition() = delete;
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 
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  */
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 
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 };
140 
141 } // namespace event
142 
143 using event_t = event::event;
144 using event_source_t = event::source;
145 using event_cond_t = event::condition;
146 
147 } // namespace sdbusplus
148