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