Line data Source code
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 : LibeventScheduler(); 61 : 62 : // Scheduler 63 : TimerPtr createTimer(const TimerCb& cb, Dispatcher& dispatcher) override; 64 : SchedulableCallbackPtr createSchedulableCallback(const std::function<void()>& cb) override; 65 : 66 : /** 67 : * Runs the event loop. 68 : * 69 : * @param mode The mode in which to run the event loop. 70 : */ 71 : void run(Dispatcher::RunType mode); 72 : 73 : /** 74 : * Exits the libevent loop. 75 : */ 76 : void loopExit(); 77 : 78 : /** 79 : * TODO(jmarantz): consider strengthening this abstraction and instead of 80 : * exposing the libevent base pointer, provide API abstractions for the calls 81 : * into it. Among other benefits this might make it more tractable to someday 82 : * consider an alternative to libevent if the need arises. 83 : * 84 : * @return the underlying libevent structure. 85 : */ 86 3803 : event_base& base() { return *libevent_; } 87 : 88 : /** 89 : * Register callback to be called in the event loop prior to polling for 90 : * events. Must not be called more than once. |callback| must not be null. 91 : * |callback| cannot be unregistered, therefore it has to be valid throughout 92 : * the lifetime of |this|. 93 : */ 94 : void registerOnPrepareCallback(OnPrepareCallback&& callback); 95 : 96 : /** 97 : * Start writing stats once thread-local storage is ready to receive them (see 98 : * ThreadLocalStoreImpl::initializeThreading). 99 : */ 100 : void initializeStats(DispatcherStats* stats); 101 : 102 : private: 103 : static void onPrepareForCallback(evwatch*, const evwatch_prepare_cb_info* info, void* arg); 104 : static void onPrepareForStats(evwatch*, const evwatch_prepare_cb_info* info, void* arg); 105 : static void onCheckForStats(evwatch*, const evwatch_check_cb_info*, void* arg); 106 : 107 17249 : static constexpr int flagsBasedOnEventType() { 108 17249 : if constexpr (Event::PlatformDefaultTriggerType == FileTriggerType::Level) { 109 : // With level events, EVLOOP_NONBLOCK will cause the libevent event_base_loop to run 110 : // forever. This is because the write event callbacks will trigger every time through the 111 : // loop. Adding EVLOOP_ONCE ensures the loop will run at most once 112 0 : return EVLOOP_NONBLOCK | EVLOOP_ONCE; 113 0 : } 114 17249 : return EVLOOP_NONBLOCK; 115 17249 : } 116 : 117 : Libevent::BasePtr libevent_; 118 : DispatcherStats* stats_{}; // stats owned by the containing DispatcherImpl 119 : bool timeout_set_{}; // whether there is a poll timeout in the current event loop iteration 120 : timeval timeout_{}; // the poll timeout for the current event loop iteration, if available 121 : timeval prepare_time_{}; // timestamp immediately before polling 122 : timeval check_time_{}; // timestamp immediately after polling 123 : OnPrepareCallback callback_; // callback to be called from onPrepareForCallback() 124 : }; 125 : 126 : } // namespace Event 127 : } // namespace Envoy