/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 |