Line data Source code
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 4 : void recordTimeval(Stats::Histogram& histogram, const timeval& tv) { 14 4 : histogram.recordValue(tv.tv_sec * 1000000 + tv.tv_usec); 15 4 : } 16 : } // namespace 17 : 18 1498 : 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 1498 : event_base* event_base = event_base_new(); 30 1498 : #endif 31 1498 : RELEASE_ASSERT(event_base != nullptr, "Failed to initialize libevent event_base"); 32 1498 : libevent_ = Libevent::BasePtr(event_base); 33 : 34 : // The dispatcher won't work as expected if libevent hasn't been configured to use threads. 35 1498 : RELEASE_ASSERT(Libevent::Global::initialized(), ""); 36 1498 : } 37 : 38 11384 : TimerPtr LibeventScheduler::createTimer(const TimerCb& cb, Dispatcher& dispatcher) { 39 11384 : return std::make_unique<TimerImpl>(libevent_, cb, dispatcher); 40 11384 : }; 41 : 42 : SchedulableCallbackPtr 43 8329 : LibeventScheduler::createSchedulableCallback(const std::function<void()>& cb) { 44 8329 : return std::make_unique<SchedulableCallbackImpl>(libevent_, cb); 45 8329 : }; 46 : 47 18604 : void LibeventScheduler::run(Dispatcher::RunType mode) { 48 18604 : int flag = 0; 49 18604 : switch (mode) { 50 17249 : case Dispatcher::RunType::NonBlock: 51 17249 : flag = LibeventScheduler::flagsBasedOnEventType(); 52 18382 : case Dispatcher::RunType::Block: 53 : // The default flags have 'block' behavior. See 54 : // http://www.wangafu.net/~nickm/libevent-book/Ref3_eventloop.html 55 18382 : break; 56 222 : case Dispatcher::RunType::RunUntilExit: 57 222 : flag = EVLOOP_NO_EXIT_ON_EMPTY; 58 222 : break; 59 18604 : } 60 18604 : event_base_loop(libevent_.get(), flag); 61 18604 : } 62 : 63 1633 : void LibeventScheduler::loopExit() { event_base_loopexit(libevent_.get(), nullptr); } 64 : 65 1498 : void LibeventScheduler::registerOnPrepareCallback(OnPrepareCallback&& callback) { 66 1498 : ASSERT(callback); 67 1498 : ASSERT(!callback_); 68 : 69 1498 : callback_ = std::move(callback); 70 1498 : evwatch_prepare_new(libevent_.get(), &onPrepareForCallback, this); 71 1498 : } 72 : 73 2 : void LibeventScheduler::initializeStats(DispatcherStats* stats) { 74 2 : stats_ = stats; 75 : // These are thread safe. 76 2 : evwatch_prepare_new(libevent_.get(), &onPrepareForStats, this); 77 2 : evwatch_check_new(libevent_.get(), &onCheckForStats, this); 78 2 : } 79 : 80 28616 : void LibeventScheduler::onPrepareForCallback(evwatch*, const evwatch_prepare_cb_info*, void* arg) { 81 : // `self` is `this`, passed in from evwatch_prepare_new. 82 28616 : auto self = static_cast<LibeventScheduler*>(arg); 83 28616 : self->callback_(); 84 28616 : } 85 : 86 : void LibeventScheduler::onPrepareForStats(evwatch*, const evwatch_prepare_cb_info* info, 87 3 : void* arg) { 88 : // `self` is `this`, passed in from evwatch_prepare_new. 89 3 : 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 3 : self->timeout_set_ = evwatch_prepare_get_timeout(info, &self->timeout_); 96 3 : 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 3 : if (self->check_time_.tv_sec != 0) { 101 1 : timeval delta; 102 1 : evutil_timersub(&self->prepare_time_, &self->check_time_, &delta); 103 1 : recordTimeval(self->stats_->loop_duration_us_, delta); 104 1 : } 105 3 : } 106 : 107 3 : void LibeventScheduler::onCheckForStats(evwatch*, const evwatch_check_cb_info*, void* arg) { 108 : // `self` is `this`, passed in from evwatch_check_new. 109 3 : 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 3 : evutil_gettimeofday(&self->check_time_, nullptr); 115 3 : if (self->timeout_set_) { 116 3 : timeval delta, delay; 117 3 : evutil_timersub(&self->check_time_, &self->prepare_time_, &delta); 118 3 : 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 3 : if (delay.tv_sec >= 0) { 125 3 : recordTimeval(self->stats_->poll_delay_us_, delay); 126 3 : } 127 3 : } 128 3 : } 129 : 130 : } // namespace Event 131 : } // namespace Envoy