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
10850094
void recordTimeval(Stats::Histogram& histogram, const timeval& tv) {
14
10850094
  histogram.recordValue(tv.tv_sec * 1000000 + tv.tv_usec);
15
10850094
}
16
} // namespace
17

            
18
77828
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
77828
  event_base* event_base = event_base_new();
30
77828
#endif
31
77828
  RELEASE_ASSERT(event_base != nullptr, "Failed to initialize libevent event_base");
32
77828
  libevent_ = Libevent::BasePtr(event_base);
33

            
34
  // The dispatcher won't work as expected if libevent hasn't been configured to use threads.
35
77828
  RELEASE_ASSERT(Libevent::Global::initialized(), "");
36
77828
}
37

            
38
401198
TimerPtr LibeventScheduler::createTimer(const TimerCb& cb, Dispatcher& dispatcher) {
39
401198
  return std::make_unique<TimerImpl>(libevent_, cb, dispatcher);
40
401198
};
41

            
42
SchedulableCallbackPtr
43
427417
LibeventScheduler::createSchedulableCallback(const std::function<void()>& cb) {
44
427417
  return std::make_unique<SchedulableCallbackImpl>(libevent_, cb);
45
427417
};
46

            
47
255757002
void LibeventScheduler::run(Dispatcher::RunType mode) {
48
255757002
  int flag = 0;
49
255757002
  switch (mode) {
50
255661848
  case Dispatcher::RunType::NonBlock:
51
255661848
    flag = LibeventScheduler::flagsBasedOnEventType();
52
255661848
    break;
53
73503
  case Dispatcher::RunType::Block:
54
    // The default flags have 'block' behavior. See
55
    // http://www.wangafu.net/~nickm/libevent-book/Ref3_eventloop.html
56
73503
    break;
57
21651
  case Dispatcher::RunType::RunUntilExit:
58
21651
    flag = EVLOOP_NO_EXIT_ON_EMPTY;
59
21651
    break;
60
255757002
  }
61
255757002
  event_base_loop(libevent_.get(), flag);
62
255757002
}
63

            
64
123275
void LibeventScheduler::loopExit() { event_base_loopexit(libevent_.get(), nullptr); }
65

            
66
6
void LibeventScheduler::registerOnPrepareCallback(OnPrepareCallback&& callback) {
67
6
  ASSERT(callback);
68
6
  ASSERT(!prepare_callback_);
69

            
70
6
  prepare_callback_ = std::move(callback);
71
6
  evwatch_prepare_new(libevent_.get(), &onPrepareForCallback, this);
72
6
}
73

            
74
77806
void LibeventScheduler::registerOnCheckCallback(OnCheckCallback&& callback) {
75
77806
  ASSERT(callback);
76
77806
  ASSERT(!check_callback_);
77

            
78
77806
  check_callback_ = std::move(callback);
79
77806
  evwatch_check_new(libevent_.get(), &onCheckForCallback, this);
80
77806
}
81

            
82
15
void LibeventScheduler::initializeStats(DispatcherStats* stats) {
83
15
  stats_ = stats;
84
  // These are thread safe.
85
15
  evwatch_prepare_new(libevent_.get(), &onPrepareForStats, this);
86
15
  evwatch_check_new(libevent_.get(), &onCheckForStats, this);
87
15
}
88

            
89
308
void LibeventScheduler::onPrepareForCallback(evwatch*, const evwatch_prepare_cb_info*, void* arg) {
90
  // `self` is `this`, passed in from evwatch_prepare_new.
91
308
  auto self = static_cast<LibeventScheduler*>(arg);
92
308
  self->prepare_callback_();
93
308
}
94

            
95
264499390
void LibeventScheduler::onCheckForCallback(evwatch*, const evwatch_check_cb_info*, void* arg) {
96
  // `self` is `this`, passed in from evwatch_prepare_new.
97
264499390
  auto self = static_cast<LibeventScheduler*>(arg);
98
264499390
  self->check_callback_();
99
264499390
}
100

            
101
void LibeventScheduler::onPrepareForStats(evwatch*, const evwatch_prepare_cb_info* info,
102
5425568
                                          void* arg) {
103
  // `self` is `this`, passed in from evwatch_prepare_new.
104
5425568
  auto self = static_cast<LibeventScheduler*>(arg);
105

            
106
  // Record poll timeout and prepare time for this iteration of the event loop. The timeout is the
107
  // expected polling duration, whereas the actual polling duration will be the difference measured
108
  // between the prepare time and the check time immediately after polling. These are compared in
109
  // onCheckForStats to compute the poll_delay stat.
110
5425568
  self->timeout_set_ = evwatch_prepare_get_timeout(info, &self->timeout_);
111
5425568
  evutil_gettimeofday(&self->prepare_time_, nullptr);
112

            
113
  // If we have a check time available from a previous iteration of the event loop (that is, all but
114
  // the first), compute the loop_duration stat.
115
5425568
  if (self->check_time_.tv_sec != 0) {
116
5425553
    timeval delta;
117
5425553
    evutil_timersub(&self->prepare_time_, &self->check_time_, &delta);
118
5425553
    recordTimeval(self->stats_->loop_duration_us_, delta);
119
5425553
  }
120
5425568
}
121

            
122
5425568
void LibeventScheduler::onCheckForStats(evwatch*, const evwatch_check_cb_info*, void* arg) {
123
  // `self` is `this`, passed in from evwatch_check_new.
124
5425568
  auto self = static_cast<LibeventScheduler*>(arg);
125

            
126
  // Record check time for this iteration of the event loop. Use this together with prepare time
127
  // from above to compute the actual polling duration, and store it for the next iteration of the
128
  // event loop to compute the loop duration.
129
5425568
  evutil_gettimeofday(&self->check_time_, nullptr);
130
5425568
  if (self->timeout_set_) {
131
5425568
    timeval delta, delay;
132
5425568
    evutil_timersub(&self->check_time_, &self->prepare_time_, &delta);
133
5425568
    evutil_timersub(&delta, &self->timeout_, &delay);
134

            
135
    // Delay can be negative, meaning polling completed early. This happens in normal operation,
136
    // either because I/O was ready before we hit the timeout, or just because the kernel was
137
    // feeling saucy. Disregard negative delays in stats, since they don't indicate anything
138
    // particularly useful.
139
5425568
    if (delay.tv_sec >= 0) {
140
5424541
      recordTimeval(self->stats_->poll_delay_us_, delay);
141
5424541
    }
142
5425568
  }
143
5425568
}
144

            
145
} // namespace Event
146
} // namespace Envoy