Coverage Report

Created: 2023-11-12 09:30

/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
  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
35.9k
  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
58.0k
  static constexpr int flagsBasedOnEventType() {
108
58.0k
    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
58.0k
    return EVLOOP_NONBLOCK;
115
58.0k
  }
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