Add MemoryIdler suppot to IOThreadPoolExecutor
[folly.git] / folly / io / async / EventBase.cpp
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef __STDC_FORMAT_MACROS
18 #define __STDC_FORMAT_MACROS
19 #endif
20
21 #include <folly/io/async/EventBase.h>
22
23 #include <folly/ThreadName.h>
24 #include <folly/io/async/NotificationQueue.h>
25
26 #include <boost/static_assert.hpp>
27 #include <fcntl.h>
28 #include <pthread.h>
29 #include <unistd.h>
30
31 namespace {
32
33 using folly::Cob;
34 using folly::EventBase;
35
36 template <typename Callback>
37 class FunctionLoopCallback : public EventBase::LoopCallback {
38  public:
39   explicit FunctionLoopCallback(Cob&& function)
40       : function_(std::move(function)) {}
41
42   explicit FunctionLoopCallback(const Cob& function)
43       : function_(function) {}
44
45   virtual void runLoopCallback() noexcept {
46     function_();
47     delete this;
48   }
49
50  private:
51   Callback function_;
52 };
53
54 }
55
56 namespace folly {
57
58 const int kNoFD = -1;
59
60 /*
61  * EventBase::FunctionRunner
62  */
63
64 class EventBase::FunctionRunner
65     : public NotificationQueue<std::pair<void (*)(void*), void*>>::Consumer {
66  public:
67   void messageAvailable(std::pair<void (*)(void*), void*>&& msg) {
68
69     // In libevent2, internal events do not break the loop.
70     // Most users would expect loop(), followed by runInEventBaseThread(),
71     // to break the loop and check if it should exit or not.
72     // To have similar bejaviour to libevent1.4, tell the loop to break here.
73     // Note that loop() may still continue to loop, but it will also check the
74     // stop_ flag as well as runInLoop callbacks, etc.
75     event_base_loopbreak(getEventBase()->evb_);
76
77     if (msg.first == nullptr && msg.second == nullptr) {
78       // terminateLoopSoon() sends a null message just to
79       // wake up the loop.  We can ignore these messages.
80       return;
81     }
82
83     // If function is nullptr, just log and move on
84     if (!msg.first) {
85       LOG(ERROR) << "nullptr callback registered to be run in "
86                  << "event base thread";
87       return;
88     }
89
90     // The function should never throw an exception, because we have no
91     // way of knowing what sort of error handling to perform.
92     //
93     // If it does throw, log a message and abort the program.
94     try {
95       msg.first(msg.second);
96     } catch (const std::exception& ex) {
97       LOG(ERROR) << "runInEventBaseThread() function threw a "
98                  << typeid(ex).name() << " exception: " << ex.what();
99       abort();
100     } catch (...) {
101       LOG(ERROR) << "runInEventBaseThread() function threw an exception";
102       abort();
103     }
104   }
105 };
106
107 /*
108  * EventBase::CobTimeout methods
109  */
110
111 void EventBase::CobTimeout::timeoutExpired() noexcept {
112   // For now, we just swallow any exceptions that the callback threw.
113   try {
114     cob_();
115   } catch (const std::exception& ex) {
116     LOG(ERROR) << "EventBase::runAfterDelay() callback threw "
117                << typeid(ex).name() << " exception: " << ex.what();
118   } catch (...) {
119     LOG(ERROR) << "EventBase::runAfterDelay() callback threw non-exception "
120                << "type";
121   }
122
123   // The CobTimeout object was allocated on the heap by runAfterDelay(),
124   // so delete it now that the it has fired.
125   delete this;
126 }
127
128 /*
129  * EventBase methods
130  */
131
132 EventBase::EventBase()
133   : runOnceCallbacks_(nullptr)
134   , stop_(false)
135   , loopThread_(0)
136   , evb_(static_cast<event_base*>(event_init()))
137   , queue_(nullptr)
138   , fnRunner_(nullptr)
139   , maxLatency_(0)
140   , avgLoopTime_(2000000)
141   , maxLatencyLoopTime_(avgLoopTime_)
142   , nextLoopCnt_(-40)       // Early wrap-around so bugs will manifest soon
143   , latestLoopCnt_(nextLoopCnt_)
144   , startWork_(0)
145   , observer_(nullptr)
146   , observerSampleCount_(0) {
147   if (UNLIKELY(evb_ == nullptr)) {
148     LOG(ERROR) << "EventBase(): Failed to init event base.";
149     folly::throwSystemError("error in EventBase::EventBase()");
150   }
151   VLOG(5) << "EventBase(): Created.";
152   initNotificationQueue();
153   RequestContext::getStaticContext();
154 }
155
156 // takes ownership of the event_base
157 EventBase::EventBase(event_base* evb)
158   : runOnceCallbacks_(nullptr)
159   , stop_(false)
160   , loopThread_(0)
161   , evb_(evb)
162   , queue_(nullptr)
163   , fnRunner_(nullptr)
164   , maxLatency_(0)
165   , avgLoopTime_(2000000)
166   , maxLatencyLoopTime_(avgLoopTime_)
167   , nextLoopCnt_(-40)       // Early wrap-around so bugs will manifest soon
168   , latestLoopCnt_(nextLoopCnt_)
169   , startWork_(0)
170   , observer_(nullptr)
171   , observerSampleCount_(0) {
172   if (UNLIKELY(evb_ == nullptr)) {
173     LOG(ERROR) << "EventBase(): Pass nullptr as event base.";
174     throw std::invalid_argument("EventBase(): event base cannot be nullptr");
175   }
176   initNotificationQueue();
177   RequestContext::getStaticContext();
178 }
179
180 EventBase::~EventBase() {
181   // Call all destruction callbacks, before we start cleaning up our state.
182   while (!onDestructionCallbacks_.empty()) {
183     LoopCallback* callback = &onDestructionCallbacks_.front();
184     onDestructionCallbacks_.pop_front();
185     callback->runLoopCallback();
186   }
187
188   // Delete any unfired callback objects, so that we don't leak memory
189   // (Note that we don't fire them.  The caller is responsible for cleaning up
190   // its own data structures if it destroys the EventBase with unfired events
191   // remaining.)
192   while (!pendingCobTimeouts_.empty()) {
193     CobTimeout* timeout = &pendingCobTimeouts_.front();
194     delete timeout;
195   }
196
197   while (!noWaitLoopCallbacks_.empty()) {
198     delete &noWaitLoopCallbacks_.front();
199   }
200
201   (void) runLoopCallbacks(false);
202
203   // Stop consumer before deleting NotificationQueue
204   fnRunner_->stopConsuming();
205   event_base_free(evb_);
206   VLOG(5) << "EventBase(): Destroyed.";
207 }
208
209 int EventBase::getNotificationQueueSize() const {
210   return queue_->size();
211 }
212
213 void EventBase::setMaxReadAtOnce(uint32_t maxAtOnce) {
214   fnRunner_->setMaxReadAtOnce(maxAtOnce);
215 }
216
217 // Set smoothing coefficient for loop load average; input is # of milliseconds
218 // for exp(-1) decay.
219 void EventBase::setLoadAvgMsec(uint32_t ms) {
220   uint64_t us = 1000 * ms;
221   if (ms > 0) {
222     maxLatencyLoopTime_.setTimeInterval(us);
223     avgLoopTime_.setTimeInterval(us);
224   } else {
225     LOG(ERROR) << "non-positive arg to setLoadAvgMsec()";
226   }
227 }
228
229 void EventBase::resetLoadAvg(double value) {
230   avgLoopTime_.reset(value);
231   maxLatencyLoopTime_.reset(value);
232 }
233
234 static std::chrono::milliseconds
235 getTimeDelta(std::chrono::steady_clock::time_point* prev) {
236   auto result = std::chrono::steady_clock::now() - *prev;
237   *prev = std::chrono::steady_clock::now();
238
239   return std::chrono::duration_cast<std::chrono::milliseconds>(result);
240 }
241
242 void EventBase::waitUntilRunning() {
243   while (!isRunning()) {
244     sched_yield();
245   }
246 }
247
248 // enters the event_base loop -- will only exit when forced to
249 bool EventBase::loop() {
250   return loopBody();
251 }
252
253 bool EventBase::loopOnce(int flags) {
254   return loopBody(flags | EVLOOP_ONCE);
255 }
256
257 bool EventBase::loopBody(int flags) {
258   VLOG(5) << "EventBase(): Starting loop.";
259   int res = 0;
260   bool ranLoopCallbacks;
261   bool blocking = !(flags & EVLOOP_NONBLOCK);
262   bool once = (flags & EVLOOP_ONCE);
263
264   loopThread_.store(pthread_self(), std::memory_order_release);
265
266   if (!name_.empty()) {
267     setThreadName(name_);
268   }
269
270   auto prev = std::chrono::steady_clock::now();
271   int64_t idleStart = std::chrono::duration_cast<std::chrono::microseconds>(
272     std::chrono::steady_clock::now().time_since_epoch()).count();
273
274   // TODO: Read stop_ atomically with an acquire barrier.
275   while (!stop_) {
276     ++nextLoopCnt_;
277
278     // nobody can add loop callbacks from within this thread if
279     // we don't have to handle anything to start with...
280     if (blocking && loopCallbacks_.empty()) {
281       LoopCallbackList callbacks;
282       callbacks.swap(noWaitLoopCallbacks_);
283
284       while(!callbacks.empty()) {
285         auto* item = &callbacks.front();
286         callbacks.pop_front();
287         item->runLoopCallback();
288       }
289
290       res = event_base_loop(evb_, EVLOOP_ONCE);
291     } else {
292       res = event_base_loop(evb_, EVLOOP_ONCE | EVLOOP_NONBLOCK);
293     }
294
295     ranLoopCallbacks = runLoopCallbacks();
296
297     int64_t busy = std::chrono::duration_cast<std::chrono::microseconds>(
298       std::chrono::steady_clock::now().time_since_epoch()).count() - startWork_;
299     int64_t idle = startWork_ - idleStart;
300
301     avgLoopTime_.addSample(idle, busy);
302     maxLatencyLoopTime_.addSample(idle, busy);
303
304     if (observer_) {
305       if (observerSampleCount_++ == observer_->getSampleRate()) {
306         observerSampleCount_ = 0;
307         observer_->loopSample(busy, idle);
308       }
309     }
310
311     VLOG(11) << "EventBase " << this         << " did not timeout "
312      " loop time guess: "    << busy + idle  <<
313      " idle time: "          << idle         <<
314      " busy time: "          << busy         <<
315      " avgLoopTime: "        << avgLoopTime_.get() <<
316      " maxLatencyLoopTime: " << maxLatencyLoopTime_.get() <<
317      " maxLatency_: "        << maxLatency_ <<
318      " nothingHandledYet(): "<< nothingHandledYet();
319
320     // see if our average loop time has exceeded our limit
321     if ((maxLatency_ > 0) &&
322         (maxLatencyLoopTime_.get() > double(maxLatency_))) {
323       maxLatencyCob_();
324       // back off temporarily -- don't keep spamming maxLatencyCob_
325       // if we're only a bit over the limit
326       maxLatencyLoopTime_.dampen(0.9);
327     }
328
329     // Our loop run did real work; reset the idle timer
330     idleStart = std::chrono::duration_cast<std::chrono::microseconds>(
331       std::chrono::steady_clock::now().time_since_epoch()).count();
332
333     // If the event loop indicate that there were no more events, and
334     // we also didn't have any loop callbacks to run, there is nothing left to
335     // do.
336     if (res != 0 && !ranLoopCallbacks) {
337       // Since Notification Queue is marked 'internal' some events may not have
338       // run.  Run them manually if so, and continue looping.
339       //
340       if (getNotificationQueueSize() > 0) {
341         fnRunner_->handlerReady(0);
342       } else {
343         break;
344       }
345     }
346
347     VLOG(5) << "EventBase " << this << " loop time: " <<
348       getTimeDelta(&prev).count();
349
350     if (once) {
351       break;
352     }
353   }
354   // Reset stop_ so loop() can be called again
355   stop_ = false;
356
357   if (res < 0) {
358     LOG(ERROR) << "EventBase: -- error in event loop, res = " << res;
359     return false;
360   } else if (res == 1) {
361     VLOG(5) << "EventBase: ran out of events (exiting loop)!";
362   } else if (res > 1) {
363     LOG(ERROR) << "EventBase: unknown event loop result = " << res;
364     return false;
365   }
366
367   loopThread_.store(0, std::memory_order_release);
368
369   VLOG(5) << "EventBase(): Done with loop.";
370   return true;
371 }
372
373 void EventBase::loopForever() {
374   // Update the notification queue event to treat it as a normal (non-internal)
375   // event.  The notification queue event always remains installed, and the main
376   // loop won't exit with it installed.
377   fnRunner_->stopConsuming();
378   fnRunner_->startConsuming(this, queue_.get());
379
380   bool ret = loop();
381
382   // Restore the notification queue internal flag
383   fnRunner_->stopConsuming();
384   fnRunner_->startConsumingInternal(this, queue_.get());
385
386   if (!ret) {
387     folly::throwSystemError("error in EventBase::loopForever()");
388   }
389 }
390
391 bool EventBase::bumpHandlingTime() {
392   VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__ <<
393     " (loop) latest " << latestLoopCnt_ << " next " << nextLoopCnt_;
394   if(nothingHandledYet()) {
395     latestLoopCnt_ = nextLoopCnt_;
396     // set the time
397     startWork_ = std::chrono::duration_cast<std::chrono::microseconds>(
398       std::chrono::steady_clock::now().time_since_epoch()).count();
399
400     VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__ <<
401       " (loop) startWork_ " << startWork_;
402     return true;
403   }
404   return false;
405 }
406
407 void EventBase::terminateLoopSoon() {
408   VLOG(5) << "EventBase(): Received terminateLoopSoon() command.";
409
410   // Set stop to true, so the event loop will know to exit.
411   // TODO: We should really use an atomic operation here with a release
412   // barrier.
413   stop_ = true;
414
415   // Call event_base_loopbreak() so that libevent will exit the next time
416   // around the loop.
417   event_base_loopbreak(evb_);
418
419   // If terminateLoopSoon() is called from another thread,
420   // the EventBase thread might be stuck waiting for events.
421   // In this case, it won't wake up and notice that stop_ is set until it
422   // receives another event.  Send an empty frame to the notification queue
423   // so that the event loop will wake up even if there are no other events.
424   //
425   // We don't care about the return value of trySendFrame().  If it fails
426   // this likely means the EventBase already has lots of events waiting
427   // anyway.
428   try {
429     queue_->putMessage(std::make_pair(nullptr, nullptr));
430   } catch (...) {
431     // We don't care if putMessage() fails.  This likely means
432     // the EventBase already has lots of events waiting anyway.
433   }
434 }
435
436 void EventBase::runInLoop(LoopCallback* callback, bool thisIteration) {
437   DCHECK(isInEventBaseThread());
438   callback->cancelLoopCallback();
439   callback->context_ = RequestContext::saveContext();
440   if (runOnceCallbacks_ != nullptr && thisIteration) {
441     runOnceCallbacks_->push_back(*callback);
442   } else {
443     loopCallbacks_.push_back(*callback);
444   }
445 }
446
447 void EventBase::runInLoop(const Cob& cob, bool thisIteration) {
448   DCHECK(isInEventBaseThread());
449   auto wrapper = new FunctionLoopCallback<Cob>(cob);
450   wrapper->context_ = RequestContext::saveContext();
451   if (runOnceCallbacks_ != nullptr && thisIteration) {
452     runOnceCallbacks_->push_back(*wrapper);
453   } else {
454     loopCallbacks_.push_back(*wrapper);
455   }
456 }
457
458 void EventBase::runInLoop(Cob&& cob, bool thisIteration) {
459   DCHECK(isInEventBaseThread());
460   auto wrapper = new FunctionLoopCallback<Cob>(std::move(cob));
461   wrapper->context_ = RequestContext::saveContext();
462   if (runOnceCallbacks_ != nullptr && thisIteration) {
463     runOnceCallbacks_->push_back(*wrapper);
464   } else {
465     loopCallbacks_.push_back(*wrapper);
466   }
467 }
468
469 void EventBase::runOnDestruction(LoopCallback* callback) {
470   DCHECK(isInEventBaseThread());
471   callback->cancelLoopCallback();
472   onDestructionCallbacks_.push_back(*callback);
473 }
474
475 void EventBase::runBeforeLoop(LoopCallback* callback) {
476   DCHECK(isInEventBaseThread());
477   callback->cancelLoopCallback();
478   noWaitLoopCallbacks_.push_back(*callback);
479 }
480
481 bool EventBase::runInEventBaseThread(void (*fn)(void*), void* arg) {
482   // Send the message.
483   // It will be received by the FunctionRunner in the EventBase's thread.
484
485   // We try not to schedule nullptr callbacks
486   if (!fn) {
487     LOG(ERROR) << "EventBase " << this
488                << ": Scheduling nullptr callbacks is not allowed";
489     return false;
490   }
491
492   // Short-circuit if we are already in our event base
493   if (inRunningEventBaseThread()) {
494     runInLoop(new RunInLoopCallback(fn, arg));
495     return true;
496
497   }
498
499   try {
500     queue_->putMessage(std::make_pair(fn, arg));
501   } catch (const std::exception& ex) {
502     LOG(ERROR) << "EventBase " << this << ": failed to schedule function "
503                << fn << "for EventBase thread: " << ex.what();
504     return false;
505   }
506
507   return true;
508 }
509
510 bool EventBase::runInEventBaseThread(const Cob& fn) {
511   // Short-circuit if we are already in our event base
512   if (inRunningEventBaseThread()) {
513     runInLoop(fn);
514     return true;
515   }
516
517   Cob* fnCopy;
518   // Allocate a copy of the function so we can pass it to the other thread
519   // The other thread will delete this copy once the function has been run
520   try {
521     fnCopy = new Cob(fn);
522   } catch (const std::bad_alloc& ex) {
523     LOG(ERROR) << "failed to allocate tr::function copy "
524                << "for runInEventBaseThread()";
525     return false;
526   }
527
528   if (!runInEventBaseThread(&EventBase::runFunctionPtr, fnCopy)) {
529     delete fnCopy;
530     return false;
531   }
532
533   return true;
534 }
535
536 bool EventBase::runAfterDelay(const Cob& cob,
537                                int milliseconds,
538                                TimeoutManager::InternalEnum in) {
539   CobTimeout* timeout = new CobTimeout(this, cob, in);
540   if (!timeout->scheduleTimeout(milliseconds)) {
541     delete timeout;
542     return false;
543   }
544
545   pendingCobTimeouts_.push_back(*timeout);
546   return true;
547 }
548
549 bool EventBase::runLoopCallbacks(bool setContext) {
550   if (!loopCallbacks_.empty()) {
551     bumpHandlingTime();
552     // Swap the loopCallbacks_ list with a temporary list on our stack.
553     // This way we will only run callbacks scheduled at the time
554     // runLoopCallbacks() was invoked.
555     //
556     // If any of these callbacks in turn call runInLoop() to schedule more
557     // callbacks, those new callbacks won't be run until the next iteration
558     // around the event loop.  This prevents runInLoop() callbacks from being
559     // able to start file descriptor and timeout based events.
560     LoopCallbackList currentCallbacks;
561     currentCallbacks.swap(loopCallbacks_);
562     runOnceCallbacks_ = &currentCallbacks;
563
564     while (!currentCallbacks.empty()) {
565       LoopCallback* callback = &currentCallbacks.front();
566       currentCallbacks.pop_front();
567       if (setContext) {
568         RequestContext::setContext(callback->context_);
569       }
570       callback->runLoopCallback();
571     }
572
573     runOnceCallbacks_ = nullptr;
574     return true;
575   }
576   return false;
577 }
578
579 void EventBase::initNotificationQueue() {
580   // Infinite size queue
581   queue_.reset(new NotificationQueue<std::pair<void (*)(void*), void*>>());
582
583   // We allocate fnRunner_ separately, rather than declaring it directly
584   // as a member of EventBase solely so that we don't need to include
585   // NotificationQueue.h from EventBase.h
586   fnRunner_.reset(new FunctionRunner());
587
588   // Mark this as an internal event, so event_base_loop() will return if
589   // there are no other events besides this one installed.
590   //
591   // Most callers don't care about the internal notification queue used by
592   // EventBase.  The queue is always installed, so if we did count the queue as
593   // an active event, loop() would never exit with no more events to process.
594   // Users can use loopForever() if they do care about the notification queue.
595   // (This is useful for EventBase threads that do nothing but process
596   // runInEventBaseThread() notifications.)
597   fnRunner_->startConsumingInternal(this, queue_.get());
598 }
599
600 void EventBase::SmoothLoopTime::setTimeInterval(uint64_t timeInterval) {
601   expCoeff_ = -1.0/timeInterval;
602   VLOG(11) << "expCoeff_ " << expCoeff_ << " " << __PRETTY_FUNCTION__;
603 }
604
605 void EventBase::SmoothLoopTime::reset(double value) {
606   value_ = value;
607 }
608
609 void EventBase::SmoothLoopTime::addSample(int64_t idle, int64_t busy) {
610     /*
611      * Position at which the busy sample is considered to be taken.
612      * (Allows to quickly skew our average without editing much code)
613      */
614     enum BusySamplePosition {
615       RIGHT = 0,  // busy sample placed at the end of the iteration
616       CENTER = 1, // busy sample placed at the middle point of the iteration
617       LEFT = 2,   // busy sample placed at the beginning of the iteration
618     };
619
620   VLOG(11) << "idle " << idle << " oldBusyLeftover_ " << oldBusyLeftover_ <<
621               " idle + oldBusyLeftover_ " << idle + oldBusyLeftover_ <<
622               " busy " << busy << " " << __PRETTY_FUNCTION__;
623   idle += oldBusyLeftover_ + busy;
624   oldBusyLeftover_ = (busy * BusySamplePosition::CENTER) / 2;
625   idle -= oldBusyLeftover_;
626
627   double coeff = exp(idle * expCoeff_);
628   value_ *= coeff;
629   value_ += (1.0 - coeff) * busy;
630 }
631
632 bool EventBase::nothingHandledYet() {
633   VLOG(11) << "latest " << latestLoopCnt_ << " next " << nextLoopCnt_;
634   return (nextLoopCnt_ != latestLoopCnt_);
635 }
636
637 /* static */
638 void EventBase::runFunctionPtr(Cob* fn) {
639   // The function should never throw an exception, because we have no
640   // way of knowing what sort of error handling to perform.
641   //
642   // If it does throw, log a message and abort the program.
643   try {
644     (*fn)();
645   } catch (const std::exception &ex) {
646     LOG(ERROR) << "runInEventBaseThread() std::function threw a "
647                << typeid(ex).name() << " exception: " << ex.what();
648     abort();
649   } catch (...) {
650     LOG(ERROR) << "runInEventBaseThread() std::function threw an exception";
651     abort();
652   }
653
654   // The function object was allocated by runInEventBaseThread().
655   // Delete it once it has been run.
656   delete fn;
657 }
658
659 EventBase::RunInLoopCallback::RunInLoopCallback(void (*fn)(void*), void* arg)
660     : fn_(fn)
661     , arg_(arg) {}
662
663 void EventBase::RunInLoopCallback::runLoopCallback() noexcept {
664   fn_(arg_);
665   delete this;
666 }
667
668 void EventBase::attachTimeoutManager(AsyncTimeout* obj,
669                                       InternalEnum internal) {
670
671   struct event* ev = obj->getEvent();
672   assert(ev->ev_base == nullptr);
673
674   event_base_set(getLibeventBase(), ev);
675   if (internal == AsyncTimeout::InternalEnum::INTERNAL) {
676     // Set the EVLIST_INTERNAL flag
677     ev->ev_flags |= EVLIST_INTERNAL;
678   }
679 }
680
681 void EventBase::detachTimeoutManager(AsyncTimeout* obj) {
682   cancelTimeout(obj);
683   struct event* ev = obj->getEvent();
684   ev->ev_base = nullptr;
685 }
686
687 bool EventBase::scheduleTimeout(AsyncTimeout* obj,
688                                  std::chrono::milliseconds timeout) {
689   assert(isInEventBaseThread());
690   // Set up the timeval and add the event
691   struct timeval tv;
692   tv.tv_sec = timeout.count() / 1000LL;
693   tv.tv_usec = (timeout.count() % 1000LL) * 1000LL;
694
695   struct event* ev = obj->getEvent();
696   if (event_add(ev, &tv) < 0) {
697     LOG(ERROR) << "EventBase: failed to schedule timeout: " << strerror(errno);
698     return false;
699   }
700
701   return true;
702 }
703
704 void EventBase::cancelTimeout(AsyncTimeout* obj) {
705   assert(isInEventBaseThread());
706   struct event* ev = obj->getEvent();
707   if (EventUtil::isEventRegistered(ev)) {
708     event_del(ev);
709   }
710 }
711
712 void EventBase::setName(const std::string& name) {
713   assert(isInEventBaseThread());
714   name_ = name;
715
716   if (isRunning()) {
717     setThreadName(loopThread_.load(std::memory_order_relaxed),
718                   name_);
719   }
720 }
721
722 const std::string& EventBase::getName() {
723   assert(isInEventBaseThread());
724   return name_;
725 }
726
727 const char* EventBase::getLibeventVersion() { return event_get_version(); }
728 const char* EventBase::getLibeventMethod() { return event_get_method(); }
729
730 } // folly