EventBase::~EventBase() { // Delete any unfired CobTimeout objects, so that we don't leak memory // (Note that we don't fire them. The caller is responsible for cleaning up // its own data structures if it destroys the EventBase with unfired events // remaining.) while (!pendingCobTimeouts_.empty()) { CobTimeout* timeout = &pendingCobTimeouts_.front(); delete timeout; } (void) runLoopCallbacks(false); // Stop consumer before deleting NotificationQueue fnRunner_->stopConsuming(); event_base_free(evb_); VLOG(5) << "EventBase(): Destroyed."; }
// enters the event_base loop -- will only exit when forced to bool EventBase::loop() { VLOG(5) << "EventBase(): Starting loop."; int res = 0; bool ranLoopCallbacks; int nonBlocking; loopThread_.store(pthread_self(), std::memory_order_release); #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 12) if (!name_.empty()) { pthread_setname_np(pthread_self(), name_.c_str()); } #endif int64_t prev = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now().time_since_epoch()).count(); int64_t idleStart = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::steady_clock::now().time_since_epoch()).count(); // TODO: Read stop_ atomically with an acquire barrier. while (!stop_) { ++nextLoopCnt_; // nobody can add loop callbacks from within this thread if // we don't have to handle anything to start with... nonBlocking = (loopCallbacks_.empty() ? 0 : EVLOOP_NONBLOCK); res = event_base_loop(evb_, EVLOOP_ONCE | nonBlocking); ranLoopCallbacks = runLoopCallbacks(); int64_t busy = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::steady_clock::now().time_since_epoch()).count() - startWork_; int64_t idle = startWork_ - idleStart; avgLoopTime_.addSample(idle, busy); maxLatencyLoopTime_.addSample(idle, busy); if (observer_) { if (observerSampleCount_++ == observer_->getSampleRate()) { observerSampleCount_ = 0; observer_->loopSample(busy, idle); } } VLOG(11) << "EventBase " << this << " did not timeout " " loop time guess: " << busy + idle << " idle time: " << idle << " busy time: " << busy << " avgLoopTime: " << avgLoopTime_.get() << " maxLatencyLoopTime: " << maxLatencyLoopTime_.get() << " maxLatency_: " << maxLatency_ << " nothingHandledYet(): "<< nothingHandledYet(); // see if our average loop time has exceeded our limit if ((maxLatency_ > 0) && (maxLatencyLoopTime_.get() > double(maxLatency_))) { maxLatencyCob_(); // back off temporarily -- don't keep spamming maxLatencyCob_ // if we're only a bit over the limit maxLatencyLoopTime_.dampen(0.9); } // Our loop run did real work; reset the idle timer idleStart = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::steady_clock::now().time_since_epoch()).count(); // If the event loop indicate that there were no more events, and // we also didn't have any loop callbacks to run, there is nothing left to // do. if (res != 0 && !ranLoopCallbacks) { // Since Notification Queue is marked 'internal' some events may not have // run. Run them manually if so, and continue looping. // if (getNotificationQueueSize() > 0) { fnRunner_->handlerReady(0); } else { break; } } VLOG(5) << "EventBase " << this << " loop time: " << getTimeDelta(&prev); } // Reset stop_ so loop() can be called again stop_ = false; if (res < 0) { LOG(ERROR) << "EventBase: -- error in event loop, res = " << res; return false; } else if (res == 1) { VLOG(5) << "EventBase: ran out of events (exiting loop)!"; } else if (res > 1) { LOG(ERROR) << "EventBase: unknown event loop result = " << res; return false; } loopThread_.store(0, std::memory_order_release); VLOG(5) << "EventBase(): Done with loop."; return true; }
bool EventBase::loopBody(int flags) { VLOG(5) << "EventBase(): Starting loop."; DCHECK(!invokingLoop_) << "Your code just tried to loop over an event base from inside another " << "event base loop. Since libevent is not reentrant, this leads to " << "undefined behavior in opt builds. Please fix immediately. For the " << "common case of an inner function that needs to do some synchronous " << "computation on an event-base, replace getEventBase() by a new, " << "stack-allocated EvenBase."; invokingLoop_ = true; SCOPE_EXIT { invokingLoop_ = false; }; int res = 0; bool ranLoopCallbacks; bool blocking = !(flags & EVLOOP_NONBLOCK); bool once = (flags & EVLOOP_ONCE); // time-measurement variables. std::chrono::steady_clock::time_point prev; int64_t idleStart = 0; int64_t busy; int64_t idle; loopThread_.store(pthread_self(), std::memory_order_release); if (!name_.empty()) { setThreadName(name_); } if (enableTimeMeasurement_) { prev = std::chrono::steady_clock::now(); idleStart = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::steady_clock::now().time_since_epoch()).count(); } while (!stop_.load(std::memory_order_acquire)) { applyLoopKeepAlive(); ++nextLoopCnt_; // Run the before loop callbacks LoopCallbackList callbacks; callbacks.swap(runBeforeLoopCallbacks_); while(!callbacks.empty()) { auto* item = &callbacks.front(); callbacks.pop_front(); item->runLoopCallback(); } // nobody can add loop callbacks from within this thread if // we don't have to handle anything to start with... if (blocking && loopCallbacks_.empty()) { res = event_base_loop(evb_, EVLOOP_ONCE); } else { res = event_base_loop(evb_, EVLOOP_ONCE | EVLOOP_NONBLOCK); } ranLoopCallbacks = runLoopCallbacks(); if (enableTimeMeasurement_) { busy = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::steady_clock::now().time_since_epoch()).count() - startWork_; idle = startWork_ - idleStart; avgLoopTime_.addSample(idle, busy); maxLatencyLoopTime_.addSample(idle, busy); if (observer_) { if (observerSampleCount_++ == observer_->getSampleRate()) { observerSampleCount_ = 0; observer_->loopSample(busy, idle); } } VLOG(11) << "EventBase " << this << " did not timeout " " loop time guess: " << busy + idle << " idle time: " << idle << " busy time: " << busy << " avgLoopTime: " << avgLoopTime_.get() << " maxLatencyLoopTime: " << maxLatencyLoopTime_.get() << " maxLatency_: " << maxLatency_ << " notificationQueueSize: " << getNotificationQueueSize() << " nothingHandledYet(): "<< nothingHandledYet(); // see if our average loop time has exceeded our limit if ((maxLatency_ > 0) && (maxLatencyLoopTime_.get() > double(maxLatency_))) { maxLatencyCob_(); // back off temporarily -- don't keep spamming maxLatencyCob_ // if we're only a bit over the limit maxLatencyLoopTime_.dampen(0.9); } // Our loop run did real work; reset the idle timer idleStart = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::steady_clock::now().time_since_epoch()).count(); } else { VLOG(11) << "EventBase " << this << " did not timeout"; } // If the event loop indicate that there were no more events, and // we also didn't have any loop callbacks to run, there is nothing left to // do. if (res != 0 && !ranLoopCallbacks) { // Since Notification Queue is marked 'internal' some events may not have // run. Run them manually if so, and continue looping. // if (getNotificationQueueSize() > 0) { fnRunner_->handlerReady(0); } else { break; } } if (enableTimeMeasurement_) { VLOG(5) << "EventBase " << this << " loop time: " << getTimeDelta(&prev).count(); } if (once) { break; } } // Reset stop_ so loop() can be called again stop_ = false; if (res < 0) { LOG(ERROR) << "EventBase: -- error in event loop, res = " << res; return false; } else if (res == 1) { VLOG(5) << "EventBase: ran out of events (exiting loop)!"; } else if (res > 1) { LOG(ERROR) << "EventBase: unknown event loop result = " << res; return false; } loopThread_.store({}, std::memory_order_release); VLOG(5) << "EventBase(): Done with loop."; return true; }