bool EventBase::runLoopCallbacks(bool setContext) { if (!loopCallbacks_.empty()) { bumpHandlingTime(); // Swap the loopCallbacks_ list with a temporary list on our stack. // This way we will only run callbacks scheduled at the time // runLoopCallbacks() was invoked. // // If any of these callbacks in turn call runInLoop() to schedule more // callbacks, those new callbacks won't be run until the next iteration // around the event loop. This prevents runInLoop() callbacks from being // able to start file descriptor and timeout based events. LoopCallbackList currentCallbacks; currentCallbacks.swap(loopCallbacks_); runOnceCallbacks_ = ¤tCallbacks; while (!currentCallbacks.empty()) { LoopCallback* callback = ¤tCallbacks.front(); currentCallbacks.pop_front(); if (setContext) { RequestContext::setContext(callback->context_); } callback->runLoopCallback(); } runOnceCallbacks_ = nullptr; return true; } return false; }
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; }