Coverage Report

Created: 2024-09-19 09:45

/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