Example #1
0
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_ = &currentCallbacks;

    while (!currentCallbacks.empty()) {
      LoopCallback* callback = &currentCallbacks.front();
      currentCallbacks.pop_front();
      if (setContext) {
        RequestContext::setContext(callback->context_);
      }
      callback->runLoopCallback();
    }

    runOnceCallbacks_ = nullptr;
    return true;
  }
  return false;
}
Example #2
0
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;
}