Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/event/libevent_scheduler.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/event/libevent_scheduler.h"
2
3
#include "source/common/common/assert.h"
4
#include "source/common/event/schedulable_cb_impl.h"
5
#include "source/common/event/timer_impl.h"
6
7
#include "event2/util.h"
8
9
namespace Envoy {
10
namespace Event {
11
12
namespace {
13
194
void recordTimeval(Stats::Histogram& histogram, const timeval& tv) {
14
194
  histogram.recordValue(tv.tv_sec * 1000000 + tv.tv_usec);
15
194
}
16
} // namespace
17
18
33.1k
LibeventScheduler::LibeventScheduler() {
19
#ifdef WIN32
20
  event_config* event_config = event_config_new();
21
  RELEASE_ASSERT(event_config != nullptr,
22
                 "Failed to initialize libevent event_base: event_config_new");
23
  // Request wepoll backend by avoiding win32 backend.
24
  int error = event_config_avoid_method(event_config, "win32");
25
  RELEASE_ASSERT(error == 0, "Failed to initialize libevent event_base: event_config_avoid_method");
26
  event_base* event_base = event_base_new_with_config(event_config);
27
  event_config_free(event_config);
28
#else
29
33.1k
  event_base* event_base = event_base_new();
30
33.1k
#endif
31
33.1k
  RELEASE_ASSERT(event_base != nullptr, "Failed to initialize libevent event_base");
32
33.1k
  libevent_ = Libevent::BasePtr(event_base);
33
34
  // The dispatcher won't work as expected if libevent hasn't been configured to use threads.
35
33.1k
  RELEASE_ASSERT(Libevent::Global::initialized(), "");
36
33.1k
}
37
38
277k
TimerPtr LibeventScheduler::createTimer(const TimerCb& cb, Dispatcher& dispatcher) {
39
277k
  return std::make_unique<TimerImpl>(libevent_, cb, dispatcher);
40
277k
};
41
42
SchedulableCallbackPtr
43
147k
LibeventScheduler::createSchedulableCallback(const std::function<void()>& cb) {
44
147k
  return std::make_unique<SchedulableCallbackImpl>(libevent_, cb);
45
147k
};
46
47
78.5k
void LibeventScheduler::run(Dispatcher::RunType mode) {
48
78.5k
  int flag = 0;
49
78.5k
  switch (mode) {
50
58.0k
  case Dispatcher::RunType::NonBlock:
51
58.0k
    flag = LibeventScheduler::flagsBasedOnEventType();
52
70.8k
  case Dispatcher::RunType::Block:
53
    // The default flags have 'block' behavior. See
54
    // http://www.wangafu.net/~nickm/libevent-book/Ref3_eventloop.html
55
70.8k
    break;
56
7.62k
  case Dispatcher::RunType::RunUntilExit:
57
7.62k
    flag = EVLOOP_NO_EXIT_ON_EMPTY;
58
7.62k
    break;
59
78.5k
  }
60
78.5k
  event_base_loop(libevent_.get(), flag);
61
78.5k
}
62
63
20.5k
void LibeventScheduler::loopExit() { event_base_loopexit(libevent_.get(), nullptr); }
64
65
33.1k
void LibeventScheduler::registerOnPrepareCallback(OnPrepareCallback&& callback) {
66
33.1k
  ASSERT(callback);
67
33.1k
  ASSERT(!callback_);
68
69
33.1k
  callback_ = std::move(callback);
70
33.1k
  evwatch_prepare_new(libevent_.get(), &onPrepareForCallback, this);
71
33.1k
}
72
73
78
void LibeventScheduler::initializeStats(DispatcherStats* stats) {
74
78
  stats_ = stats;
75
  // These are thread safe.
76
78
  evwatch_prepare_new(libevent_.get(), &onPrepareForStats, this);
77
78
  evwatch_check_new(libevent_.get(), &onCheckForStats, this);
78
78
}
79
80
170k
void LibeventScheduler::onPrepareForCallback(evwatch*, const evwatch_prepare_cb_info*, void* arg) {
81
  // `self` is `this`, passed in from evwatch_prepare_new.
82
170k
  auto self = static_cast<LibeventScheduler*>(arg);
83
170k
  self->callback_();
84
170k
}
85
86
void LibeventScheduler::onPrepareForStats(evwatch*, const evwatch_prepare_cb_info* info,
87
136
                                          void* arg) {
88
  // `self` is `this`, passed in from evwatch_prepare_new.
89
136
  auto self = static_cast<LibeventScheduler*>(arg);
90
91
  // Record poll timeout and prepare time for this iteration of the event loop. The timeout is the
92
  // expected polling duration, whereas the actual polling duration will be the difference measured
93
  // between the prepare time and the check time immediately after polling. These are compared in
94
  // onCheckForStats to compute the poll_delay stat.
95
136
  self->timeout_set_ = evwatch_prepare_get_timeout(info, &self->timeout_);
96
136
  evutil_gettimeofday(&self->prepare_time_, nullptr);
97
98
  // If we have a check time available from a previous iteration of the event loop (that is, all but
99
  // the first), compute the loop_duration stat.
100
136
  if (self->check_time_.tv_sec != 0) {
101
58
    timeval delta;
102
58
    evutil_timersub(&self->prepare_time_, &self->check_time_, &delta);
103
58
    recordTimeval(self->stats_->loop_duration_us_, delta);
104
58
  }
105
136
}
106
107
136
void LibeventScheduler::onCheckForStats(evwatch*, const evwatch_check_cb_info*, void* arg) {
108
  // `self` is `this`, passed in from evwatch_check_new.
109
136
  auto self = static_cast<LibeventScheduler*>(arg);
110
111
  // Record check time for this iteration of the event loop. Use this together with prepare time
112
  // from above to compute the actual polling duration, and store it for the next iteration of the
113
  // event loop to compute the loop duration.
114
136
  evutil_gettimeofday(&self->check_time_, nullptr);
115
136
  if (self->timeout_set_) {
116
136
    timeval delta, delay;
117
136
    evutil_timersub(&self->check_time_, &self->prepare_time_, &delta);
118
136
    evutil_timersub(&delta, &self->timeout_, &delay);
119
120
    // Delay can be negative, meaning polling completed early. This happens in normal operation,
121
    // either because I/O was ready before we hit the timeout, or just because the kernel was
122
    // feeling saucy. Disregard negative delays in stats, since they don't indicate anything
123
    // particularly useful.
124
136
    if (delay.tv_sec >= 0) {
125
136
      recordTimeval(self->stats_->poll_delay_us_, delay);
126
136
    }
127
136
  }
128
136
}
129
130
} // namespace Event
131
} // namespace Envoy