/proc/self/cwd/source/common/event/libevent_scheduler.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <functional> |
4 | | |
5 | | #include "envoy/event/dispatcher.h" |
6 | | #include "envoy/event/schedulable_cb.h" |
7 | | #include "envoy/event/timer.h" |
8 | | |
9 | | #include "source/common/event/libevent.h" |
10 | | |
11 | | #include "event2/event.h" |
12 | | #include "event2/watch.h" |
13 | | |
14 | | namespace Envoy { |
15 | | namespace Event { |
16 | | |
17 | | // Implements Scheduler based on libevent. |
18 | | // |
19 | | // Here is a rough summary of operations that libevent performs in each event loop iteration, in |
20 | | // order. Note that the invocation order for "same-iteration" operations that execute as a group |
21 | | // can be surprising and invocation order of expired timers is non-deterministic. |
22 | | // Whenever possible, it is preferable to avoid making event invocation ordering assumptions. |
23 | | // |
24 | | // 1. Calculate the poll timeout by comparing the current time to the deadline of the closest |
25 | | // timer (the one at head of the priority queue). |
26 | | // 2. Run registered "prepare" callbacks. |
27 | | // 3. Poll for fd events using the closest timer as timeout, add active fds to the work list. |
28 | | // 4. Run registered "check" callbacks. |
29 | | // 5. Check timer deadlines against current time and move expired timers from the timer priority |
30 | | // queue to the work list. Expired timers are moved to the work list is a non-deterministic order. |
31 | | // 6. Execute items in the work list until the list is empty. Note that additional work |
32 | | // items could be added to the work list during execution of this step, more details below. |
33 | | // 7. Goto 1 if the loop termination condition has not been reached |
34 | | // |
35 | | // The following "same-iteration" work items are added directly to the work list when they are |
36 | | // scheduled so they execute in the current iteration of the event loop. Note that there are no |
37 | | // ordering guarantees when mixing the mechanisms below. Specifically, it is unsafe to assume that |
38 | | // calling post followed by deferredDelete will result in the post callback being invoked before the |
39 | | // deferredDelete; deferredDelete will run first if there is a pending deferredDeletion at the time |
40 | | // the post callback is scheduled because deferredDelete invocation is grouped. |
41 | | // - Event::Dispatcher::post(cb). Post callbacks are invoked as a group. |
42 | | // - Event::Dispatcher::deferredDelete(object) and Event::DeferredTaskUtil::deferredRun(...). |
43 | | // The same mechanism implements both of these operations, so they are invoked as a group. |
44 | | // - Event::SchedulableCallback::scheduleCallbackCurrentIteration(). Each of these callbacks is |
45 | | // scheduled and invoked independently. |
46 | | // |
47 | | // Event::FileEvent::activate and Event::SchedulableCallback::scheduleCallbackNextIteration are |
48 | | // implemented as libevent timers with a deadline of 0. Both of these actions are moved to the work |
49 | | // list while checking for expired timers during step 5. |
50 | | // |
51 | | // Events execute in the following order, derived from the order in which items were added to the |
52 | | // work list: |
53 | | // 0. Events added via event_active prior to the start of the event loop (in tests) |
54 | | // 1. Fd events |
55 | | // 2. Timers, FileEvent::activate and SchedulableCallback::scheduleCallbackNextIteration |
56 | | // 3. "Same-iteration" work items described above, including Event::Dispatcher::post callbacks |
57 | | class LibeventScheduler : public Scheduler, public CallbackScheduler { |
58 | | public: |
59 | | using OnPrepareCallback = std::function<void()>; |
60 | | using OnCheckCallback = std::function<void()>; |
61 | | |
62 | | LibeventScheduler(); |
63 | | |
64 | | // Scheduler |
65 | | TimerPtr createTimer(const TimerCb& cb, Dispatcher& dispatcher) override; |
66 | | SchedulableCallbackPtr createSchedulableCallback(const std::function<void()>& cb) override; |
67 | | |
68 | | /** |
69 | | * Runs the event loop. |
70 | | * |
71 | | * @param mode The mode in which to run the event loop. |
72 | | */ |
73 | | void run(Dispatcher::RunType mode); |
74 | | |
75 | | /** |
76 | | * Exits the libevent loop. |
77 | | */ |
78 | | void loopExit(); |
79 | | |
80 | | /** |
81 | | * TODO(jmarantz): consider strengthening this abstraction and instead of |
82 | | * exposing the libevent base pointer, provide API abstractions for the calls |
83 | | * into it. Among other benefits this might make it more tractable to someday |
84 | | * consider an alternative to libevent if the need arises. |
85 | | * |
86 | | * @return the underlying libevent structure. |
87 | | */ |
88 | 23.2k | event_base& base() { return *libevent_; } |
89 | | |
90 | | /** |
91 | | * Register callback to be called in the event loop prior to polling for |
92 | | * events. Must not be called more than once. |callback| must not be null. |
93 | | * |callback| cannot be unregistered, therefore it has to be valid throughout |
94 | | * the lifetime of |this|. |
95 | | */ |
96 | | void registerOnPrepareCallback(OnPrepareCallback&& callback); |
97 | | |
98 | | /** |
99 | | * Register callback to be called in the event loop after polling for |
100 | | * events and prior to handling those events. Must not be called more than once. |callback| must |
101 | | * not be null. |callback| cannot be unregistered, therefore it has to be valid throughout the |
102 | | * lifetime of |this|. |
103 | | */ |
104 | | void registerOnCheckCallback(OnCheckCallback&& callback); |
105 | | |
106 | | /** |
107 | | * Start writing stats once thread-local storage is ready to receive them (see |
108 | | * ThreadLocalStoreImpl::initializeThreading). |
109 | | */ |
110 | | void initializeStats(DispatcherStats* stats); |
111 | | |
112 | | private: |
113 | | static void onPrepareForCallback(evwatch*, const evwatch_prepare_cb_info* info, void* arg); |
114 | | static void onCheckForCallback(evwatch*, const evwatch_check_cb_info* info, void* arg); |
115 | | static void onPrepareForStats(evwatch*, const evwatch_prepare_cb_info* info, void* arg); |
116 | | static void onCheckForStats(evwatch*, const evwatch_check_cb_info*, void* arg); |
117 | | |
118 | 135k | static constexpr int flagsBasedOnEventType() { |
119 | 135k | if constexpr (Event::PlatformDefaultTriggerType == FileTriggerType::Level) { |
120 | | // With level events, EVLOOP_NONBLOCK will cause the libevent event_base_loop to run |
121 | | // forever. This is because the write event callbacks will trigger every time through the |
122 | | // loop. Adding EVLOOP_ONCE ensures the loop will run at most once |
123 | 0 | return EVLOOP_NONBLOCK | EVLOOP_ONCE; |
124 | 0 | } |
125 | 135k | return EVLOOP_NONBLOCK; |
126 | 135k | } |
127 | | |
128 | | Libevent::BasePtr libevent_; |
129 | | DispatcherStats* stats_{}; // stats owned by the containing DispatcherImpl |
130 | | bool timeout_set_{}; // whether there is a poll timeout in the current event loop iteration |
131 | | timeval timeout_{}; // the poll timeout for the current event loop iteration, if available |
132 | | timeval prepare_time_{}; // timestamp immediately before polling |
133 | | timeval check_time_{}; // timestamp immediately after polling |
134 | | OnPrepareCallback prepare_callback_; // callback to be called from onPrepareForCallback() |
135 | | OnCheckCallback check_callback_; // callback to be called from onCheckForCallback() |
136 | | }; |
137 | | |
138 | | } // namespace Event |
139 | | } // namespace Envoy |