Coverage Report

Created: 2024-09-19 09:45

/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
261
void recordTimeval(Stats::Histogram& histogram, const timeval& tv) {
14
261
  histogram.recordValue(tv.tv_sec * 1000000 + tv.tv_usec);
15
261
}
16
} // namespace
17
18
25.0k
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
25.0k
  event_base* event_base = event_base_new();
30
25.0k
#endif
31
25.0k
  RELEASE_ASSERT(event_base != nullptr, "Failed to initialize libevent event_base");
32
25.0k
  libevent_ = Libevent::BasePtr(event_base);
33
34
  // The dispatcher won't work as expected if libevent hasn't been configured to use threads.
35
25.0k
  RELEASE_ASSERT(Libevent::Global::initialized(), "");
36
25.0k
}
37
38
39.3k
TimerPtr LibeventScheduler::createTimer(const TimerCb& cb, Dispatcher& dispatcher) {
39
39.3k
  return std::make_unique<TimerImpl>(libevent_, cb, dispatcher);
40
39.3k
};
41
42
SchedulableCallbackPtr
43
92.7k
LibeventScheduler::createSchedulableCallback(const std::function<void()>& cb) {
44
92.7k
  return std::make_unique<SchedulableCallbackImpl>(libevent_, cb);
45
92.7k
};
46
47
149k
void LibeventScheduler::run(Dispatcher::RunType mode) {
48
149k
  int flag = 0;
49
149k
  switch (mode) {
50
135k
  case Dispatcher::RunType::NonBlock:
51
135k
    flag = LibeventScheduler::flagsBasedOnEventType();
52
143k
  case Dispatcher::RunType::Block:
53
    // The default flags have 'block' behavior. See
54
    // http://www.wangafu.net/~nickm/libevent-book/Ref3_eventloop.html
55
143k
    break;
56
6.09k
  case Dispatcher::RunType::RunUntilExit:
57
6.09k
    flag = EVLOOP_NO_EXIT_ON_EMPTY;
58
6.09k
    break;
59
149k
  }
60
149k
  event_base_loop(libevent_.get(), flag);
61
149k
}
62
63
13.8k
void LibeventScheduler::loopExit() { event_base_loopexit(libevent_.get(), nullptr); }
64
65
0
void LibeventScheduler::registerOnPrepareCallback(OnPrepareCallback&& callback) {
66
0
  ASSERT(callback);
67
0
  ASSERT(!prepare_callback_);
68
69
0
  prepare_callback_ = std::move(callback);
70
0
  evwatch_prepare_new(libevent_.get(), &onPrepareForCallback, this);
71
0
}
72
73
25.0k
void LibeventScheduler::registerOnCheckCallback(OnCheckCallback&& callback) {
74
25.0k
  ASSERT(callback);
75
25.0k
  ASSERT(!check_callback_);
76
77
25.0k
  check_callback_ = std::move(callback);
78
25.0k
  evwatch_check_new(libevent_.get(), &onCheckForCallback, this);
79
25.0k
}
80
81
87
void LibeventScheduler::initializeStats(DispatcherStats* stats) {
82
87
  stats_ = stats;
83
  // These are thread safe.
84
87
  evwatch_prepare_new(libevent_.get(), &onPrepareForStats, this);
85
87
  evwatch_check_new(libevent_.get(), &onCheckForStats, this);
86
87
}
87
88
0
void LibeventScheduler::onPrepareForCallback(evwatch*, const evwatch_prepare_cb_info*, void* arg) {
89
  // `self` is `this`, passed in from evwatch_prepare_new.
90
0
  auto self = static_cast<LibeventScheduler*>(arg);
91
0
  self->prepare_callback_();
92
0
}
93
94
152k
void LibeventScheduler::onCheckForCallback(evwatch*, const evwatch_check_cb_info*, void* arg) {
95
  // `self` is `this`, passed in from evwatch_prepare_new.
96
152k
  auto self = static_cast<LibeventScheduler*>(arg);
97
152k
  self->check_callback_();
98
152k
}
99
100
void LibeventScheduler::onPrepareForStats(evwatch*, const evwatch_prepare_cb_info* info,
101
174
                                          void* arg) {
102
  // `self` is `this`, passed in from evwatch_prepare_new.
103
174
  auto self = static_cast<LibeventScheduler*>(arg);
104
105
  // Record poll timeout and prepare time for this iteration of the event loop. The timeout is the
106
  // expected polling duration, whereas the actual polling duration will be the difference measured
107
  // between the prepare time and the check time immediately after polling. These are compared in
108
  // onCheckForStats to compute the poll_delay stat.
109
174
  self->timeout_set_ = evwatch_prepare_get_timeout(info, &self->timeout_);
110
174
  evutil_gettimeofday(&self->prepare_time_, nullptr);
111
112
  // If we have a check time available from a previous iteration of the event loop (that is, all but
113
  // the first), compute the loop_duration stat.
114
174
  if (self->check_time_.tv_sec != 0) {
115
87
    timeval delta;
116
87
    evutil_timersub(&self->prepare_time_, &self->check_time_, &delta);
117
87
    recordTimeval(self->stats_->loop_duration_us_, delta);
118
87
  }
119
174
}
120
121
174
void LibeventScheduler::onCheckForStats(evwatch*, const evwatch_check_cb_info*, void* arg) {
122
  // `self` is `this`, passed in from evwatch_check_new.
123
174
  auto self = static_cast<LibeventScheduler*>(arg);
124
125
  // Record check time for this iteration of the event loop. Use this together with prepare time
126
  // from above to compute the actual polling duration, and store it for the next iteration of the
127
  // event loop to compute the loop duration.
128
174
  evutil_gettimeofday(&self->check_time_, nullptr);
129
174
  if (self->timeout_set_) {
130
174
    timeval delta, delay;
131
174
    evutil_timersub(&self->check_time_, &self->prepare_time_, &delta);
132
174
    evutil_timersub(&delta, &self->timeout_, &delay);
133
134
    // Delay can be negative, meaning polling completed early. This happens in normal operation,
135
    // either because I/O was ready before we hit the timeout, or just because the kernel was
136
    // feeling saucy. Disregard negative delays in stats, since they don't indicate anything
137
    // particularly useful.
138
174
    if (delay.tv_sec >= 0) {
139
174
      recordTimeval(self->stats_->poll_delay_us_, delay);
140
174
    }
141
174
  }
142
174
}
143
144
} // namespace Event
145
} // namespace Envoy