Introduce destruction callbacks
[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 CobTimeout 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   (void) runLoopCallbacks(false);
198
199   // Stop consumer before deleting NotificationQueue
200   fnRunner_->stopConsuming();
201   event_base_free(evb_);
202   VLOG(5) << "EventBase(): Destroyed.";
203 }
204
205 int EventBase::getNotificationQueueSize() const {
206   return queue_->size();
207 }
208
209 void EventBase::setMaxReadAtOnce(uint32_t maxAtOnce) {
210   fnRunner_->setMaxReadAtOnce(maxAtOnce);
211 }
212
213 // Set smoothing coefficient for loop load average; input is # of milliseconds
214 // for exp(-1) decay.
215 void EventBase::setLoadAvgMsec(uint32_t ms) {
216   uint64_t us = 1000 * ms;
217   if (ms > 0) {
218     maxLatencyLoopTime_.setTimeInterval(us);
219     avgLoopTime_.setTimeInterval(us);
220   } else {
221     LOG(ERROR) << "non-positive arg to setLoadAvgMsec()";
222   }
223 }
224
225 void EventBase::resetLoadAvg(double value) {
226   avgLoopTime_.reset(value);
227   maxLatencyLoopTime_.reset(value);
228 }
229
230 static std::chrono::milliseconds
231 getTimeDelta(std::chrono::steady_clock::time_point* prev) {
232   auto result = std::chrono::steady_clock::now() - *prev;
233   *prev = std::chrono::steady_clock::now();
234
235   return std::chrono::duration_cast<std::chrono::milliseconds>(result);
236 }
237
238 void EventBase::waitUntilRunning() {
239   while (!isRunning()) {
240     sched_yield();
241   }
242 }
243
244 // enters the event_base loop -- will only exit when forced to
245 bool EventBase::loop() {
246   return loopBody();
247 }
248
249 bool EventBase::loopOnce() {
250   return loopBody(true);
251 }
252
253 bool EventBase::loopBody(bool once) {
254   VLOG(5) << "EventBase(): Starting loop.";
255   int res = 0;
256   bool ranLoopCallbacks;
257   int nonBlocking;
258
259   loopThread_.store(pthread_self(), std::memory_order_release);
260
261   if (!name_.empty()) {
262     setThreadName(name_);
263   }
264
265   auto prev = std::chrono::steady_clock::now();
266   int64_t idleStart = std::chrono::duration_cast<std::chrono::microseconds>(
267     std::chrono::steady_clock::now().time_since_epoch()).count();
268
269   // TODO: Read stop_ atomically with an acquire barrier.
270   while (!stop_) {
271     ++nextLoopCnt_;
272
273     // nobody can add loop callbacks from within this thread if
274     // we don't have to handle anything to start with...
275     nonBlocking = (loopCallbacks_.empty() ? 0 : EVLOOP_NONBLOCK);
276     res = event_base_loop(evb_, EVLOOP_ONCE | nonBlocking);
277     ranLoopCallbacks = runLoopCallbacks();
278
279     int64_t busy = std::chrono::duration_cast<std::chrono::microseconds>(
280       std::chrono::steady_clock::now().time_since_epoch()).count() - startWork_;
281     int64_t idle = startWork_ - idleStart;
282
283     avgLoopTime_.addSample(idle, busy);
284     maxLatencyLoopTime_.addSample(idle, busy);
285
286     if (observer_) {
287       if (observerSampleCount_++ == observer_->getSampleRate()) {
288         observerSampleCount_ = 0;
289         observer_->loopSample(busy, idle);
290       }
291     }
292
293     VLOG(11) << "EventBase " << this         << " did not timeout "
294      " loop time guess: "    << busy + idle  <<
295      " idle time: "          << idle         <<
296      " busy time: "          << busy         <<
297      " avgLoopTime: "        << avgLoopTime_.get() <<
298      " maxLatencyLoopTime: " << maxLatencyLoopTime_.get() <<
299      " maxLatency_: "        << maxLatency_ <<
300      " nothingHandledYet(): "<< nothingHandledYet();
301
302     // see if our average loop time has exceeded our limit
303     if ((maxLatency_ > 0) &&
304         (maxLatencyLoopTime_.get() > double(maxLatency_))) {
305       maxLatencyCob_();
306       // back off temporarily -- don't keep spamming maxLatencyCob_
307       // if we're only a bit over the limit
308       maxLatencyLoopTime_.dampen(0.9);
309     }
310
311     // Our loop run did real work; reset the idle timer
312     idleStart = std::chrono::duration_cast<std::chrono::microseconds>(
313       std::chrono::steady_clock::now().time_since_epoch()).count();
314
315     // If the event loop indicate that there were no more events, and
316     // we also didn't have any loop callbacks to run, there is nothing left to
317     // do.
318     if (res != 0 && !ranLoopCallbacks) {
319       // Since Notification Queue is marked 'internal' some events may not have
320       // run.  Run them manually if so, and continue looping.
321       //
322       if (getNotificationQueueSize() > 0) {
323         fnRunner_->handlerReady(0);
324       } else {
325         break;
326       }
327     }
328
329     VLOG(5) << "EventBase " << this << " loop time: " <<
330       getTimeDelta(&prev).count();
331
332     if (once) {
333       break;
334     }
335   }
336   // Reset stop_ so loop() can be called again
337   stop_ = false;
338
339   if (res < 0) {
340     LOG(ERROR) << "EventBase: -- error in event loop, res = " << res;
341     return false;
342   } else if (res == 1) {
343     VLOG(5) << "EventBase: ran out of events (exiting loop)!";
344   } else if (res > 1) {
345     LOG(ERROR) << "EventBase: unknown event loop result = " << res;
346     return false;
347   }
348
349   loopThread_.store(0, std::memory_order_release);
350
351   VLOG(5) << "EventBase(): Done with loop.";
352   return true;
353 }
354
355 void EventBase::loopForever() {
356   // Update the notification queue event to treat it as a normal (non-internal)
357   // event.  The notification queue event always remains installed, and the main
358   // loop won't exit with it installed.
359   fnRunner_->stopConsuming();
360   fnRunner_->startConsuming(this, queue_.get());
361
362   bool ret = loop();
363
364   // Restore the notification queue internal flag
365   fnRunner_->stopConsuming();
366   fnRunner_->startConsumingInternal(this, queue_.get());
367
368   if (!ret) {
369     folly::throwSystemError("error in EventBase::loopForever()");
370   }
371 }
372
373 bool EventBase::bumpHandlingTime() {
374   VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__ <<
375     " (loop) latest " << latestLoopCnt_ << " next " << nextLoopCnt_;
376   if(nothingHandledYet()) {
377     latestLoopCnt_ = nextLoopCnt_;
378     // set the time
379     startWork_ = std::chrono::duration_cast<std::chrono::microseconds>(
380       std::chrono::steady_clock::now().time_since_epoch()).count();
381
382     VLOG(11) << "EventBase " << this << " " << __PRETTY_FUNCTION__ <<
383       " (loop) startWork_ " << startWork_;
384     return true;
385   }
386   return false;
387 }
388
389 void EventBase::terminateLoopSoon() {
390   VLOG(5) << "EventBase(): Received terminateLoopSoon() command.";
391
392   if (!isRunning()) {
393     return;
394   }
395
396   // Set stop to true, so the event loop will know to exit.
397   // TODO: We should really use an atomic operation here with a release
398   // barrier.
399   stop_ = true;
400
401   // Call event_base_loopbreak() so that libevent will exit the next time
402   // around the loop.
403   event_base_loopbreak(evb_);
404
405   // If terminateLoopSoon() is called from another thread,
406   // the EventBase thread might be stuck waiting for events.
407   // In this case, it won't wake up and notice that stop_ is set until it
408   // receives another event.  Send an empty frame to the notification queue
409   // so that the event loop will wake up even if there are no other events.
410   //
411   // We don't care about the return value of trySendFrame().  If it fails
412   // this likely means the EventBase already has lots of events waiting
413   // anyway.
414   try {
415     queue_->putMessage(std::make_pair(nullptr, nullptr));
416   } catch (...) {
417     // We don't care if putMessage() fails.  This likely means
418     // the EventBase already has lots of events waiting anyway.
419   }
420 }
421
422 void EventBase::runInLoop(LoopCallback* callback, bool thisIteration) {
423   DCHECK(isInEventBaseThread());
424   callback->cancelLoopCallback();
425   callback->context_ = RequestContext::saveContext();
426   if (runOnceCallbacks_ != nullptr && thisIteration) {
427     runOnceCallbacks_->push_back(*callback);
428   } else {
429     loopCallbacks_.push_back(*callback);
430   }
431 }
432
433 void EventBase::runInLoop(const Cob& cob, bool thisIteration) {
434   DCHECK(isInEventBaseThread());
435   auto wrapper = new FunctionLoopCallback<Cob>(cob);
436   wrapper->context_ = RequestContext::saveContext();
437   if (runOnceCallbacks_ != nullptr && thisIteration) {
438     runOnceCallbacks_->push_back(*wrapper);
439   } else {
440     loopCallbacks_.push_back(*wrapper);
441   }
442 }
443
444 void EventBase::runInLoop(Cob&& cob, bool thisIteration) {
445   DCHECK(isInEventBaseThread());
446   auto wrapper = new FunctionLoopCallback<Cob>(std::move(cob));
447   wrapper->context_ = RequestContext::saveContext();
448   if (runOnceCallbacks_ != nullptr && thisIteration) {
449     runOnceCallbacks_->push_back(*wrapper);
450   } else {
451     loopCallbacks_.push_back(*wrapper);
452   }
453 }
454
455 void EventBase::runOnDestruction(LoopCallback* callback) {
456   DCHECK(isInEventBaseThread());
457   callback->cancelLoopCallback();
458   onDestructionCallbacks_.push_back(*callback);
459 }
460
461 bool EventBase::runInEventBaseThread(void (*fn)(void*), void* arg) {
462   // Send the message.
463   // It will be received by the FunctionRunner in the EventBase's thread.
464
465   // We try not to schedule nullptr callbacks
466   if (!fn) {
467     LOG(ERROR) << "EventBase " << this
468                << ": Scheduling nullptr callbacks is not allowed";
469     return false;
470   }
471
472   // Short-circuit if we are already in our event base
473   if (inRunningEventBaseThread()) {
474     runInLoop(new RunInLoopCallback(fn, arg));
475     return true;
476
477   }
478
479   try {
480     queue_->putMessage(std::make_pair(fn, arg));
481   } catch (const std::exception& ex) {
482     LOG(ERROR) << "EventBase " << this << ": failed to schedule function "
483                << fn << "for EventBase thread: " << ex.what();
484     return false;
485   }
486
487   return true;
488 }
489
490 bool EventBase::runInEventBaseThread(const Cob& fn) {
491   // Short-circuit if we are already in our event base
492   if (inRunningEventBaseThread()) {
493     runInLoop(fn);
494     return true;
495   }
496
497   Cob* fnCopy;
498   // Allocate a copy of the function so we can pass it to the other thread
499   // The other thread will delete this copy once the function has been run
500   try {
501     fnCopy = new Cob(fn);
502   } catch (const std::bad_alloc& ex) {
503     LOG(ERROR) << "failed to allocate tr::function copy "
504                << "for runInEventBaseThread()";
505     return false;
506   }
507
508   if (!runInEventBaseThread(&EventBase::runFunctionPtr, fnCopy)) {
509     delete fnCopy;
510     return false;
511   }
512
513   return true;
514 }
515
516 bool EventBase::runAfterDelay(const Cob& cob,
517                                int milliseconds,
518                                TimeoutManager::InternalEnum in) {
519   CobTimeout* timeout = new CobTimeout(this, cob, in);
520   if (!timeout->scheduleTimeout(milliseconds)) {
521     delete timeout;
522     return false;
523   }
524
525   pendingCobTimeouts_.push_back(*timeout);
526   return true;
527 }
528
529 bool EventBase::runLoopCallbacks(bool setContext) {
530   if (!loopCallbacks_.empty()) {
531     bumpHandlingTime();
532     // Swap the loopCallbacks_ list with a temporary list on our stack.
533     // This way we will only run callbacks scheduled at the time
534     // runLoopCallbacks() was invoked.
535     //
536     // If any of these callbacks in turn call runInLoop() to schedule more
537     // callbacks, those new callbacks won't be run until the next iteration
538     // around the event loop.  This prevents runInLoop() callbacks from being
539     // able to start file descriptor and timeout based events.
540     LoopCallbackList currentCallbacks;
541     currentCallbacks.swap(loopCallbacks_);
542     runOnceCallbacks_ = &currentCallbacks;
543
544     while (!currentCallbacks.empty()) {
545       LoopCallback* callback = &currentCallbacks.front();
546       currentCallbacks.pop_front();
547       if (setContext) {
548         RequestContext::setContext(callback->context_);
549       }
550       callback->runLoopCallback();
551     }
552
553     runOnceCallbacks_ = nullptr;
554     return true;
555   }
556   return false;
557 }
558
559 void EventBase::initNotificationQueue() {
560   // Infinite size queue
561   queue_.reset(new NotificationQueue<std::pair<void (*)(void*), void*>>());
562
563   // We allocate fnRunner_ separately, rather than declaring it directly
564   // as a member of EventBase solely so that we don't need to include
565   // NotificationQueue.h from EventBase.h
566   fnRunner_.reset(new FunctionRunner());
567
568   // Mark this as an internal event, so event_base_loop() will return if
569   // there are no other events besides this one installed.
570   //
571   // Most callers don't care about the internal notification queue used by
572   // EventBase.  The queue is always installed, so if we did count the queue as
573   // an active event, loop() would never exit with no more events to process.
574   // Users can use loopForever() if they do care about the notification queue.
575   // (This is useful for EventBase threads that do nothing but process
576   // runInEventBaseThread() notifications.)
577   fnRunner_->startConsumingInternal(this, queue_.get());
578 }
579
580 void EventBase::SmoothLoopTime::setTimeInterval(uint64_t timeInterval) {
581   expCoeff_ = -1.0/timeInterval;
582   VLOG(11) << "expCoeff_ " << expCoeff_ << " " << __PRETTY_FUNCTION__;
583 }
584
585 void EventBase::SmoothLoopTime::reset(double value) {
586   value_ = value;
587 }
588
589 void EventBase::SmoothLoopTime::addSample(int64_t idle, int64_t busy) {
590     /*
591      * Position at which the busy sample is considered to be taken.
592      * (Allows to quickly skew our average without editing much code)
593      */
594     enum BusySamplePosition {
595       RIGHT = 0,  // busy sample placed at the end of the iteration
596       CENTER = 1, // busy sample placed at the middle point of the iteration
597       LEFT = 2,   // busy sample placed at the beginning of the iteration
598     };
599
600   VLOG(11) << "idle " << idle << " oldBusyLeftover_ " << oldBusyLeftover_ <<
601               " idle + oldBusyLeftover_ " << idle + oldBusyLeftover_ <<
602               " busy " << busy << " " << __PRETTY_FUNCTION__;
603   idle += oldBusyLeftover_ + busy;
604   oldBusyLeftover_ = (busy * BusySamplePosition::CENTER) / 2;
605   idle -= oldBusyLeftover_;
606
607   double coeff = exp(idle * expCoeff_);
608   value_ *= coeff;
609   value_ += (1.0 - coeff) * busy;
610 }
611
612 bool EventBase::nothingHandledYet() {
613   VLOG(11) << "latest " << latestLoopCnt_ << " next " << nextLoopCnt_;
614   return (nextLoopCnt_ != latestLoopCnt_);
615 }
616
617 /* static */
618 void EventBase::runFunctionPtr(Cob* fn) {
619   // The function should never throw an exception, because we have no
620   // way of knowing what sort of error handling to perform.
621   //
622   // If it does throw, log a message and abort the program.
623   try {
624     (*fn)();
625   } catch (const std::exception &ex) {
626     LOG(ERROR) << "runInEventBaseThread() std::function threw a "
627                << typeid(ex).name() << " exception: " << ex.what();
628     abort();
629   } catch (...) {
630     LOG(ERROR) << "runInEventBaseThread() std::function threw an exception";
631     abort();
632   }
633
634   // The function object was allocated by runInEventBaseThread().
635   // Delete it once it has been run.
636   delete fn;
637 }
638
639 EventBase::RunInLoopCallback::RunInLoopCallback(void (*fn)(void*), void* arg)
640     : fn_(fn)
641     , arg_(arg) {}
642
643 void EventBase::RunInLoopCallback::runLoopCallback() noexcept {
644   fn_(arg_);
645   delete this;
646 }
647
648 void EventBase::attachTimeoutManager(AsyncTimeout* obj,
649                                       InternalEnum internal) {
650
651   struct event* ev = obj->getEvent();
652   assert(ev->ev_base == nullptr);
653
654   event_base_set(getLibeventBase(), ev);
655   if (internal == AsyncTimeout::InternalEnum::INTERNAL) {
656     // Set the EVLIST_INTERNAL flag
657     ev->ev_flags |= EVLIST_INTERNAL;
658   }
659 }
660
661 void EventBase::detachTimeoutManager(AsyncTimeout* obj) {
662   cancelTimeout(obj);
663   struct event* ev = obj->getEvent();
664   ev->ev_base = nullptr;
665 }
666
667 bool EventBase::scheduleTimeout(AsyncTimeout* obj,
668                                  std::chrono::milliseconds timeout) {
669   assert(isInEventBaseThread());
670   // Set up the timeval and add the event
671   struct timeval tv;
672   tv.tv_sec = timeout.count() / 1000LL;
673   tv.tv_usec = (timeout.count() % 1000LL) * 1000LL;
674
675   struct event* ev = obj->getEvent();
676   if (event_add(ev, &tv) < 0) {
677     LOG(ERROR) << "EventBase: failed to schedule timeout: " << strerror(errno);
678     return false;
679   }
680
681   return true;
682 }
683
684 void EventBase::cancelTimeout(AsyncTimeout* obj) {
685   assert(isInEventBaseThread());
686   struct event* ev = obj->getEvent();
687   if (EventUtil::isEventRegistered(ev)) {
688     event_del(ev);
689   }
690 }
691
692 void EventBase::setName(const std::string& name) {
693   assert(isInEventBaseThread());
694   name_ = name;
695
696   if (isRunning()) {
697     setThreadName(loopThread_.load(std::memory_order_relaxed),
698                   name_);
699   }
700 }
701
702 const std::string& EventBase::getName() {
703   assert(isInEventBaseThread());
704   return name_;
705 }
706
707 } // folly