963 lines
32 KiB
C
963 lines
32 KiB
C
|
/*
|
||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include <atomic>
|
||
|
#include <cerrno>
|
||
|
#include <cmath>
|
||
|
#include <cstdlib>
|
||
|
#include <functional>
|
||
|
#include <list>
|
||
|
#include <memory>
|
||
|
#include <queue>
|
||
|
#include <set>
|
||
|
#include <stack>
|
||
|
#include <unordered_map>
|
||
|
#include <unordered_set>
|
||
|
#include <utility>
|
||
|
|
||
|
#include <boost/intrusive/list.hpp>
|
||
|
#include <glog/logging.h>
|
||
|
|
||
|
#include <folly/Executor.h>
|
||
|
#include <folly/Function.h>
|
||
|
#include <folly/Portability.h>
|
||
|
#include <folly/ScopeGuard.h>
|
||
|
#include <folly/Synchronized.h>
|
||
|
#include <folly/executors/DrivableExecutor.h>
|
||
|
#include <folly/executors/IOExecutor.h>
|
||
|
#include <folly/executors/ScheduledExecutor.h>
|
||
|
#include <folly/executors/SequencedExecutor.h>
|
||
|
#include <folly/experimental/ExecutionObserver.h>
|
||
|
#include <folly/io/async/AsyncTimeout.h>
|
||
|
#include <folly/io/async/HHWheelTimer.h>
|
||
|
#include <folly/io/async/Request.h>
|
||
|
#include <folly/io/async/TimeoutManager.h>
|
||
|
#include <folly/portability/Event.h>
|
||
|
#include <folly/synchronization/CallOnce.h>
|
||
|
|
||
|
namespace folly {
|
||
|
class EventBaseBackendBase;
|
||
|
|
||
|
using Cob = Func; // defined in folly/Executor.h
|
||
|
template <typename MessageT>
|
||
|
class NotificationQueue;
|
||
|
|
||
|
namespace detail {
|
||
|
class EventBaseLocalBase;
|
||
|
|
||
|
class EventBaseLocalBaseBase {
|
||
|
public:
|
||
|
virtual void onEventBaseDestruction(EventBase& evb) = 0;
|
||
|
virtual ~EventBaseLocalBaseBase() = default;
|
||
|
};
|
||
|
} // namespace detail
|
||
|
template <typename T>
|
||
|
class EventBaseLocal;
|
||
|
|
||
|
class EventBaseObserver {
|
||
|
public:
|
||
|
virtual ~EventBaseObserver() = default;
|
||
|
|
||
|
virtual uint32_t getSampleRate() const = 0;
|
||
|
|
||
|
virtual void loopSample(int64_t busyTime, int64_t idleTime) = 0;
|
||
|
};
|
||
|
|
||
|
// Helper class that sets and retrieves the EventBase associated with a given
|
||
|
// request via RequestContext. See Request.h for that mechanism.
|
||
|
class RequestEventBase : public RequestData {
|
||
|
public:
|
||
|
static EventBase* get() {
|
||
|
auto data = dynamic_cast<RequestEventBase*>(
|
||
|
RequestContext::get()->getContextData(kContextDataName));
|
||
|
if (!data) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
return data->eb_;
|
||
|
}
|
||
|
|
||
|
static void set(EventBase* eb) {
|
||
|
RequestContext::get()->setContextData(
|
||
|
kContextDataName,
|
||
|
std::unique_ptr<RequestEventBase>(new RequestEventBase(eb)));
|
||
|
}
|
||
|
|
||
|
bool hasCallback() override {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
explicit RequestEventBase(EventBase* eb) : eb_(eb) {}
|
||
|
EventBase* eb_;
|
||
|
static constexpr const char* kContextDataName{"EventBase"};
|
||
|
};
|
||
|
|
||
|
class VirtualEventBase;
|
||
|
|
||
|
/**
|
||
|
* This class is a wrapper for all asynchronous I/O processing functionality
|
||
|
*
|
||
|
* EventBase provides a main loop that notifies EventHandler callback objects
|
||
|
* when I/O is ready on a file descriptor, and notifies AsyncTimeout objects
|
||
|
* when a specified timeout has expired. More complex, higher-level callback
|
||
|
* mechanisms can then be built on top of EventHandler and AsyncTimeout.
|
||
|
*
|
||
|
* A EventBase object can only drive an event loop for a single thread. To
|
||
|
* take advantage of multiple CPU cores, most asynchronous I/O servers have one
|
||
|
* thread per CPU, and use a separate EventBase for each thread.
|
||
|
*
|
||
|
* In general, most EventBase methods may only be called from the thread
|
||
|
* running the EventBase's loop. There are a few exceptions to this rule, for
|
||
|
* methods that are explicitly intended to allow communication with a
|
||
|
* EventBase from other threads. When it is safe to call a method from
|
||
|
* another thread it is explicitly listed in the method comments.
|
||
|
*/
|
||
|
class EventBase : public TimeoutManager,
|
||
|
public DrivableExecutor,
|
||
|
public IOExecutor,
|
||
|
public SequencedExecutor,
|
||
|
public ScheduledExecutor {
|
||
|
public:
|
||
|
friend class ScopedEventBaseThread;
|
||
|
|
||
|
using Func = folly::Function<void()>;
|
||
|
|
||
|
/**
|
||
|
* A callback interface to use with runInLoop()
|
||
|
*
|
||
|
* Derive from this class if you need to delay some code execution until the
|
||
|
* next iteration of the event loop. This allows you to schedule code to be
|
||
|
* invoked from the top-level of the loop, after your immediate callers have
|
||
|
* returned.
|
||
|
*
|
||
|
* If a LoopCallback object is destroyed while it is scheduled to be run in
|
||
|
* the next loop iteration, it will automatically be cancelled.
|
||
|
*/
|
||
|
class LoopCallback
|
||
|
: public boost::intrusive::list_base_hook<
|
||
|
boost::intrusive::link_mode<boost::intrusive::auto_unlink>> {
|
||
|
public:
|
||
|
virtual ~LoopCallback() = default;
|
||
|
|
||
|
virtual void runLoopCallback() noexcept = 0;
|
||
|
void cancelLoopCallback() {
|
||
|
context_.reset();
|
||
|
unlink();
|
||
|
}
|
||
|
|
||
|
bool isLoopCallbackScheduled() const {
|
||
|
return is_linked();
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
typedef boost::intrusive::
|
||
|
list<LoopCallback, boost::intrusive::constant_time_size<false>>
|
||
|
List;
|
||
|
|
||
|
// EventBase needs access to LoopCallbackList (and therefore to hook_)
|
||
|
friend class EventBase;
|
||
|
friend class VirtualEventBase;
|
||
|
std::shared_ptr<RequestContext> context_;
|
||
|
};
|
||
|
|
||
|
class FunctionLoopCallback : public LoopCallback {
|
||
|
public:
|
||
|
explicit FunctionLoopCallback(Func&& function)
|
||
|
: function_(std::move(function)) {}
|
||
|
|
||
|
void runLoopCallback() noexcept override {
|
||
|
function_();
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Func function_;
|
||
|
};
|
||
|
|
||
|
// Like FunctionLoopCallback, but saves one allocation. Use with caution.
|
||
|
//
|
||
|
// The caller is responsible for maintaining the lifetime of this callback
|
||
|
// until after the point at which the contained function is called.
|
||
|
class StackFunctionLoopCallback : public LoopCallback {
|
||
|
public:
|
||
|
explicit StackFunctionLoopCallback(Func&& function)
|
||
|
: function_(std::move(function)) {}
|
||
|
void runLoopCallback() noexcept override {
|
||
|
Func(std::move(function_))();
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Func function_;
|
||
|
};
|
||
|
|
||
|
// Base class for user callbacks to be run during EventBase destruction. As
|
||
|
// with LoopCallback, users may inherit from this class and provide a concrete
|
||
|
// implementation of onEventBaseDestruction(). (Alternatively, users may use
|
||
|
// the convenience method EventBase::runOnDestruction(Function<void()> f) to
|
||
|
// schedule a function f to be run on EventBase destruction.)
|
||
|
//
|
||
|
// The only thread-safety guarantees of OnDestructionCallback are as follows:
|
||
|
// - Users may call runOnDestruction() from any thread, provided the caller
|
||
|
// is the only user of the callback, i.e., the callback is not already
|
||
|
// scheduled and there are no concurrent calls to schedule or cancel the
|
||
|
// callback.
|
||
|
// - Users may safely cancel() from any thread. Multiple calls to cancel()
|
||
|
// may execute concurrently. The only caveat is that it is not safe to
|
||
|
// call cancel() within the onEventBaseDestruction() callback.
|
||
|
class OnDestructionCallback {
|
||
|
public:
|
||
|
OnDestructionCallback() = default;
|
||
|
OnDestructionCallback(OnDestructionCallback&&) = default;
|
||
|
OnDestructionCallback& operator=(OnDestructionCallback&&) = default;
|
||
|
virtual ~OnDestructionCallback();
|
||
|
|
||
|
// Attempt to cancel the callback. If the callback is running or has already
|
||
|
// finished running, cancellation will fail. If the callback is running when
|
||
|
// cancel() is called, cancel() will block until the callback completes.
|
||
|
bool cancel();
|
||
|
|
||
|
// Callback to be invoked during ~EventBase()
|
||
|
virtual void onEventBaseDestruction() noexcept = 0;
|
||
|
|
||
|
private:
|
||
|
boost::intrusive::list_member_hook<
|
||
|
boost::intrusive::link_mode<boost::intrusive::normal_link>>
|
||
|
listHook_;
|
||
|
Function<void(OnDestructionCallback&)> eraser_;
|
||
|
Synchronized<bool> scheduled_{in_place, false};
|
||
|
|
||
|
using List = boost::intrusive::list<
|
||
|
OnDestructionCallback,
|
||
|
boost::intrusive::member_hook<
|
||
|
OnDestructionCallback,
|
||
|
decltype(listHook_),
|
||
|
&OnDestructionCallback::listHook_>>;
|
||
|
|
||
|
void schedule(
|
||
|
FunctionRef<void(OnDestructionCallback&)> linker,
|
||
|
Function<void(OnDestructionCallback&)> eraser);
|
||
|
|
||
|
friend class EventBase;
|
||
|
friend class VirtualEventBase;
|
||
|
|
||
|
protected:
|
||
|
virtual void runCallback() noexcept;
|
||
|
};
|
||
|
|
||
|
class FunctionOnDestructionCallback : public OnDestructionCallback {
|
||
|
public:
|
||
|
explicit FunctionOnDestructionCallback(Function<void()> f)
|
||
|
: f_(std::move(f)) {}
|
||
|
|
||
|
void onEventBaseDestruction() noexcept final {
|
||
|
f_();
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
void runCallback() noexcept override {
|
||
|
OnDestructionCallback::runCallback();
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Function<void()> f_;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Create a new EventBase object.
|
||
|
*
|
||
|
* Same as EventBase(true), which constructs an EventBase that measures time,
|
||
|
* except that this also allows the timer granularity to be specified
|
||
|
*/
|
||
|
|
||
|
explicit EventBase(std::chrono::milliseconds tickInterval) : EventBase(true) {
|
||
|
intervalDuration_ = tickInterval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new EventBase object.
|
||
|
*
|
||
|
* Same as EventBase(true), which constructs an EventBase that measures time.
|
||
|
*/
|
||
|
EventBase() : EventBase(true) {}
|
||
|
|
||
|
/**
|
||
|
* Create a new EventBase object.
|
||
|
*
|
||
|
* @param enableTimeMeasurement Informs whether this event base should measure
|
||
|
* time. Disabling it would likely improve
|
||
|
* performance, but will disable some features
|
||
|
* that relies on time-measurement, including:
|
||
|
* observer, max latency and avg loop time.
|
||
|
*/
|
||
|
explicit EventBase(bool enableTimeMeasurement);
|
||
|
|
||
|
EventBase(const EventBase&) = delete;
|
||
|
EventBase& operator=(const EventBase&) = delete;
|
||
|
|
||
|
/**
|
||
|
* Create a new EventBase object that will use the specified libevent
|
||
|
* event_base object to drive the event loop.
|
||
|
*
|
||
|
* The EventBase will take ownership of this event_base, and will call
|
||
|
* event_base_free(evb) when the EventBase is destroyed.
|
||
|
*
|
||
|
* @param enableTimeMeasurement Informs whether this event base should measure
|
||
|
* time. Disabling it would likely improve
|
||
|
* performance, but will disable some features
|
||
|
* that relies on time-measurement, including:
|
||
|
* observer, max latency and avg loop time.
|
||
|
*/
|
||
|
explicit EventBase(event_base* evb, bool enableTimeMeasurement = true);
|
||
|
explicit EventBase(
|
||
|
std::unique_ptr<EventBaseBackendBase>&& evb,
|
||
|
bool enableTimeMeasurement = true);
|
||
|
~EventBase() override;
|
||
|
|
||
|
/**
|
||
|
* Runs the event loop.
|
||
|
*
|
||
|
* loop() will loop waiting for I/O or timeouts and invoking EventHandler
|
||
|
* and AsyncTimeout callbacks as their events become ready. loop() will
|
||
|
* only return when there are no more events remaining to process, or after
|
||
|
* terminateLoopSoon() has been called.
|
||
|
*
|
||
|
* loop() may be called again to restart event processing after a previous
|
||
|
* call to loop() or loopForever() has returned.
|
||
|
*
|
||
|
* Returns true if the loop completed normally (if it processed all
|
||
|
* outstanding requests, or if terminateLoopSoon() was called). If an error
|
||
|
* occurs waiting for events, false will be returned.
|
||
|
*/
|
||
|
bool loop();
|
||
|
|
||
|
/**
|
||
|
* Same as loop(), but doesn't wait for all keep-alive tokens to be released.
|
||
|
*/
|
||
|
[[deprecated("This should only be used in legacy unit tests")]] bool
|
||
|
loopIgnoreKeepAlive();
|
||
|
|
||
|
/**
|
||
|
* Wait for some events to become active, run them, then return.
|
||
|
*
|
||
|
* When EVLOOP_NONBLOCK is set in flags, the loop won't block if there
|
||
|
* are not any events to process.
|
||
|
*
|
||
|
* This is useful for callers that want to run the loop manually.
|
||
|
*
|
||
|
* Returns the same result as loop().
|
||
|
*/
|
||
|
bool loopOnce(int flags = 0);
|
||
|
|
||
|
/**
|
||
|
* Runs the event loop.
|
||
|
*
|
||
|
* loopForever() behaves like loop(), except that it keeps running even if
|
||
|
* when there are no more user-supplied EventHandlers or AsyncTimeouts
|
||
|
* registered. It will only return after terminateLoopSoon() has been
|
||
|
* called.
|
||
|
*
|
||
|
* This is useful for callers that want to wait for other threads to call
|
||
|
* runInEventBaseThread(), even when there are no other scheduled events.
|
||
|
*
|
||
|
* loopForever() may be called again to restart event processing after a
|
||
|
* previous call to loop() or loopForever() has returned.
|
||
|
*
|
||
|
* Throws a std::system_error if an error occurs.
|
||
|
*/
|
||
|
void loopForever();
|
||
|
|
||
|
/**
|
||
|
* Causes the event loop to exit soon.
|
||
|
*
|
||
|
* This will cause an existing call to loop() or loopForever() to stop event
|
||
|
* processing and return, even if there are still events remaining to be
|
||
|
* processed.
|
||
|
*
|
||
|
* It is safe to call terminateLoopSoon() from another thread to cause loop()
|
||
|
* to wake up and return in the EventBase loop thread. terminateLoopSoon()
|
||
|
* may also be called from the loop thread itself (for example, a
|
||
|
* EventHandler or AsyncTimeout callback may call terminateLoopSoon() to
|
||
|
* cause the loop to exit after the callback returns.) If the loop is not
|
||
|
* running, this will cause the next call to loop to terminate soon after
|
||
|
* starting. If a loop runs out of work (and so terminates on its own)
|
||
|
* concurrently with a call to terminateLoopSoon(), this may cause a race
|
||
|
* condition.
|
||
|
*
|
||
|
* Note that the caller is responsible for ensuring that cleanup of all event
|
||
|
* callbacks occurs properly. Since terminateLoopSoon() causes the loop to
|
||
|
* exit even when there are pending events present, there may be remaining
|
||
|
* callbacks present waiting to be invoked. If the loop is later restarted
|
||
|
* pending events will continue to be processed normally, however if the
|
||
|
* EventBase is destroyed after calling terminateLoopSoon() it is the
|
||
|
* caller's responsibility to ensure that cleanup happens properly even if
|
||
|
* some outstanding events are never processed.
|
||
|
*/
|
||
|
void terminateLoopSoon();
|
||
|
|
||
|
/**
|
||
|
* Adds the given callback to a queue of things run after the current pass
|
||
|
* through the event loop completes. Note that if this callback calls
|
||
|
* runInLoop() the new callback won't be called until the main event loop
|
||
|
* has gone through a cycle.
|
||
|
*
|
||
|
* This method may only be called from the EventBase's thread. This
|
||
|
* essentially allows an event handler to schedule an additional callback to
|
||
|
* be invoked after it returns.
|
||
|
*
|
||
|
* Use runInEventBaseThread() to schedule functions from another thread.
|
||
|
*
|
||
|
* The thisIteration parameter makes this callback run in this loop
|
||
|
* iteration, instead of the next one, even if called from a
|
||
|
* runInLoop callback (normal io callbacks that call runInLoop will
|
||
|
* always run in this iteration). This was originally added to
|
||
|
* support detachEventBase, as a user callback may have called
|
||
|
* terminateLoopSoon(), but we want to make sure we detach. Also,
|
||
|
* detachEventBase almost always must be called from the base event
|
||
|
* loop to ensure the stack is unwound, since most users of
|
||
|
* EventBase are not thread safe.
|
||
|
*
|
||
|
* Ideally we would not need thisIteration, and instead just use
|
||
|
* runInLoop with loop() (instead of terminateLoopSoon).
|
||
|
*/
|
||
|
void runInLoop(LoopCallback* callback, bool thisIteration = false);
|
||
|
|
||
|
/**
|
||
|
* Convenience function to call runInLoop() with a folly::Function.
|
||
|
*
|
||
|
* This creates a LoopCallback object to wrap the folly::Function, and invoke
|
||
|
* the folly::Function when the loop callback fires. This is slightly more
|
||
|
* expensive than defining your own LoopCallback, but more convenient in
|
||
|
* areas that aren't too performance sensitive.
|
||
|
*
|
||
|
* This method may only be called from the EventBase's thread. This
|
||
|
* essentially allows an event handler to schedule an additional callback to
|
||
|
* be invoked after it returns.
|
||
|
*
|
||
|
* Use runInEventBaseThread() to schedule functions from another thread.
|
||
|
*/
|
||
|
void runInLoop(Func c, bool thisIteration = false);
|
||
|
|
||
|
/**
|
||
|
* Adds the given callback to a queue of things run before destruction
|
||
|
* of current EventBase.
|
||
|
*
|
||
|
* This allows users of EventBase that run in it, but don't control it, to be
|
||
|
* notified before EventBase gets destructed.
|
||
|
*
|
||
|
* Note: will be called from the thread that invoked EventBase destructor,
|
||
|
* before the final run of loop callbacks.
|
||
|
*/
|
||
|
void runOnDestruction(OnDestructionCallback& callback);
|
||
|
|
||
|
/**
|
||
|
* Convenience function that allows users to pass in a Function<void()> to be
|
||
|
* run on EventBase destruction.
|
||
|
*/
|
||
|
void runOnDestruction(Func f);
|
||
|
|
||
|
/**
|
||
|
* Adds a callback that will run immediately *before* the event loop.
|
||
|
* This is very similar to runInLoop(), but will not cause the loop to break:
|
||
|
* For example, this callback could be used to get loop times.
|
||
|
*/
|
||
|
void runBeforeLoop(LoopCallback* callback);
|
||
|
|
||
|
/**
|
||
|
* Run the specified function in the EventBase's thread.
|
||
|
*
|
||
|
* This method is thread-safe, and may be called from another thread.
|
||
|
*
|
||
|
* If runInEventBaseThread() is called when the EventBase loop is not
|
||
|
* running, the function call will be delayed until the next time the loop is
|
||
|
* started.
|
||
|
*
|
||
|
* If the loop is terminated (and never later restarted) before it has a
|
||
|
* chance to run the requested function, the function will be run upon the
|
||
|
* EventBase's destruction.
|
||
|
*
|
||
|
* If two calls to runInEventBaseThread() are made from the same thread, the
|
||
|
* functions will always be run in the order that they were scheduled.
|
||
|
* Ordering between functions scheduled from separate threads is not
|
||
|
* guaranteed.
|
||
|
*
|
||
|
* @param fn The function to run. The function must not throw any
|
||
|
* exceptions.
|
||
|
* @param arg An argument to pass to the function.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
void runInEventBaseThread(void (*fn)(T*), T* arg) noexcept;
|
||
|
|
||
|
/**
|
||
|
* Run the specified function in the EventBase's thread
|
||
|
*
|
||
|
* This version of runInEventBaseThread() takes a folly::Function object.
|
||
|
* Note that this may be less efficient than the version that takes a plain
|
||
|
* function pointer and void* argument, if moving the function is expensive
|
||
|
* (e.g., if it wraps a lambda which captures some values with expensive move
|
||
|
* constructors).
|
||
|
*
|
||
|
* If the loop is terminated (and never later restarted) before it has a
|
||
|
* chance to run the requested function, the function will be run upon the
|
||
|
* EventBase's destruction.
|
||
|
*
|
||
|
* The function must not throw any exceptions.
|
||
|
*/
|
||
|
void runInEventBaseThread(Func fn) noexcept;
|
||
|
|
||
|
/**
|
||
|
* Run the specified function in the EventBase's thread.
|
||
|
*
|
||
|
* This method is thread-safe, and may be called from another thread.
|
||
|
*
|
||
|
* If runInEventBaseThreadAlwaysEnqueue() is called when the EventBase loop is
|
||
|
* not running, the function call will be delayed until the next time the loop
|
||
|
* is started.
|
||
|
*
|
||
|
* If the loop is terminated (and never later restarted) before it has a
|
||
|
* chance to run the requested function, the function will be run upon the
|
||
|
* EventBase's destruction.
|
||
|
*
|
||
|
* If two calls to runInEventBaseThreadAlwaysEnqueue() are made from the same
|
||
|
* thread, the functions will always be run in the order that they were
|
||
|
* scheduled. Ordering between functions scheduled from separate threads is
|
||
|
* not guaranteed. If a call is made from the EventBase thread, the function
|
||
|
* will not be executed inline and will be queued to the same queue as if the
|
||
|
* call would have been made from a different thread
|
||
|
*
|
||
|
* @param fn The function to run. The function must not throw any
|
||
|
* exceptions.
|
||
|
* @param arg An argument to pass to the function.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
void runInEventBaseThreadAlwaysEnqueue(void (*fn)(T*), T* arg) noexcept;
|
||
|
|
||
|
/**
|
||
|
* Run the specified function in the EventBase's thread
|
||
|
*
|
||
|
* This version of runInEventBaseThreadAlwaysEnqueue() takes a folly::Function
|
||
|
* object. Note that this may be less efficient than the version that takes a
|
||
|
* plain function pointer and void* argument, if moving the function is
|
||
|
* expensive (e.g., if it wraps a lambda which captures some values with
|
||
|
* expensive move constructors).
|
||
|
*
|
||
|
* If the loop is terminated (and never later restarted) before it has a
|
||
|
* chance to run the requested function, the function will be run upon the
|
||
|
* EventBase's destruction.
|
||
|
*
|
||
|
* The function must not throw any exceptions.
|
||
|
*/
|
||
|
void runInEventBaseThreadAlwaysEnqueue(Func fn) noexcept;
|
||
|
|
||
|
/*
|
||
|
* Like runInEventBaseThread, but the caller waits for the callback to be
|
||
|
* executed.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
void runInEventBaseThreadAndWait(void (*fn)(T*), T* arg) noexcept;
|
||
|
|
||
|
/*
|
||
|
* Like runInEventBaseThread, but the caller waits for the callback to be
|
||
|
* executed.
|
||
|
*/
|
||
|
void runInEventBaseThreadAndWait(Func fn) noexcept;
|
||
|
|
||
|
/*
|
||
|
* Like runInEventBaseThreadAndWait, except if the caller is already in the
|
||
|
* event base thread, the functor is simply run inline.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
void runImmediatelyOrRunInEventBaseThreadAndWait(
|
||
|
void (*fn)(T*),
|
||
|
T* arg) noexcept;
|
||
|
|
||
|
/*
|
||
|
* Like runInEventBaseThreadAndWait, except if the caller is already in the
|
||
|
* event base thread, the functor is simply run inline.
|
||
|
*/
|
||
|
void runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept;
|
||
|
|
||
|
/**
|
||
|
* Set the maximum desired latency in us and provide a callback which will be
|
||
|
* called when that latency is exceeded.
|
||
|
* OBS: This functionality depends on time-measurement.
|
||
|
*/
|
||
|
void setMaxLatency(std::chrono::microseconds maxLatency, Func maxLatencyCob) {
|
||
|
assert(enableTimeMeasurement_);
|
||
|
maxLatency_ = maxLatency;
|
||
|
maxLatencyCob_ = std::move(maxLatencyCob);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set smoothing coefficient for loop load average; # of milliseconds
|
||
|
* for exp(-1) (1/2.71828...) decay.
|
||
|
*/
|
||
|
void setLoadAvgMsec(std::chrono::milliseconds ms);
|
||
|
|
||
|
/**
|
||
|
* reset the load average to a desired value
|
||
|
*/
|
||
|
void resetLoadAvg(double value = 0.0);
|
||
|
|
||
|
/**
|
||
|
* Get the average loop time in microseconds (an exponentially-smoothed ave)
|
||
|
*/
|
||
|
double getAvgLoopTime() const {
|
||
|
assert(enableTimeMeasurement_);
|
||
|
return avgLoopTime_.get();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* check if the event base loop is running.
|
||
|
*/
|
||
|
bool isRunning() const {
|
||
|
return loopThread_.load(std::memory_order_relaxed) != std::thread::id();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* wait until the event loop starts (after starting the event loop thread).
|
||
|
*/
|
||
|
void waitUntilRunning();
|
||
|
|
||
|
size_t getNotificationQueueSize() const;
|
||
|
|
||
|
void setMaxReadAtOnce(uint32_t maxAtOnce);
|
||
|
|
||
|
/**
|
||
|
* Verify that current thread is the EventBase thread, if the EventBase is
|
||
|
* running.
|
||
|
*/
|
||
|
bool isInEventBaseThread() const {
|
||
|
auto tid = loopThread_.load(std::memory_order_relaxed);
|
||
|
return tid == std::thread::id() || tid == std::this_thread::get_id();
|
||
|
}
|
||
|
|
||
|
bool inRunningEventBaseThread() const {
|
||
|
return loopThread_.load(std::memory_order_relaxed) ==
|
||
|
std::this_thread::get_id();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Equivalent to CHECK(isInEventBaseThread()) (and assert/DCHECK for
|
||
|
* dcheckIsInEventBaseThread), but it prints more information on
|
||
|
* failure.
|
||
|
*/
|
||
|
void checkIsInEventBaseThread() const;
|
||
|
void dcheckIsInEventBaseThread() const {
|
||
|
if (kIsDebug) {
|
||
|
checkIsInEventBaseThread();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HHWheelTimer& timer() {
|
||
|
if (!wheelTimer_) {
|
||
|
wheelTimer_ = HHWheelTimer::newTimer(this, intervalDuration_);
|
||
|
}
|
||
|
return *wheelTimer_.get();
|
||
|
}
|
||
|
|
||
|
EventBaseBackendBase* getBackend() {
|
||
|
return evb_.get();
|
||
|
}
|
||
|
// --------- interface to underlying libevent base ------------
|
||
|
// Avoid using these functions if possible. These functions are not
|
||
|
// guaranteed to always be present if we ever provide alternative EventBase
|
||
|
// implementations that do not use libevent internally.
|
||
|
event_base* getLibeventBase() const;
|
||
|
|
||
|
static const char* getLibeventVersion();
|
||
|
static const char* getLibeventMethod();
|
||
|
|
||
|
/**
|
||
|
* only EventHandler/AsyncTimeout subclasses and ourselves should
|
||
|
* ever call this.
|
||
|
*
|
||
|
* This is used to mark the beginning of a new loop cycle by the
|
||
|
* first handler fired within that cycle.
|
||
|
*
|
||
|
*/
|
||
|
void bumpHandlingTime() final;
|
||
|
|
||
|
class SmoothLoopTime {
|
||
|
public:
|
||
|
explicit SmoothLoopTime(std::chrono::microseconds timeInterval)
|
||
|
: expCoeff_(-1.0 / timeInterval.count()), value_(0.0) {
|
||
|
VLOG(11) << "expCoeff_ " << expCoeff_ << " " << __PRETTY_FUNCTION__;
|
||
|
}
|
||
|
|
||
|
void setTimeInterval(std::chrono::microseconds timeInterval);
|
||
|
void reset(double value = 0.0);
|
||
|
|
||
|
void addSample(
|
||
|
std::chrono::microseconds total,
|
||
|
std::chrono::microseconds busy);
|
||
|
|
||
|
double get() const {
|
||
|
// Add the outstanding buffered times linearly, to avoid
|
||
|
// expensive exponentiation
|
||
|
auto lcoeff = buffer_time_.count() * -expCoeff_;
|
||
|
return value_ * (1.0 - lcoeff) + lcoeff * busy_buffer_.count();
|
||
|
}
|
||
|
|
||
|
void dampen(double factor) {
|
||
|
value_ *= factor;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
double expCoeff_;
|
||
|
double value_;
|
||
|
std::chrono::microseconds buffer_time_{0};
|
||
|
std::chrono::microseconds busy_buffer_{0};
|
||
|
std::size_t buffer_cnt_{0};
|
||
|
static constexpr std::chrono::milliseconds buffer_interval_{10};
|
||
|
};
|
||
|
|
||
|
void setObserver(const std::shared_ptr<EventBaseObserver>& observer) {
|
||
|
assert(enableTimeMeasurement_);
|
||
|
observer_ = observer;
|
||
|
}
|
||
|
|
||
|
const std::shared_ptr<EventBaseObserver>& getObserver() {
|
||
|
return observer_;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Setup execution observation/instrumentation for every EventHandler
|
||
|
* executed in this EventBase.
|
||
|
*
|
||
|
* @param executionObserver EventHandle's execution observer.
|
||
|
*/
|
||
|
void setExecutionObserver(ExecutionObserver* observer) {
|
||
|
executionObserver_ = observer;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the execution observer associated with this EventBase.
|
||
|
*/
|
||
|
ExecutionObserver* getExecutionObserver() {
|
||
|
return executionObserver_;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the name of the thread that runs this event base.
|
||
|
*/
|
||
|
void setName(const std::string& name);
|
||
|
|
||
|
/**
|
||
|
* Returns the name of the thread that runs this event base.
|
||
|
*/
|
||
|
const std::string& getName();
|
||
|
|
||
|
/// Implements the Executor interface
|
||
|
void add(Cob fn) override {
|
||
|
runInEventBaseThread(std::move(fn));
|
||
|
}
|
||
|
|
||
|
/// Implements the DrivableExecutor interface
|
||
|
void drive() override {
|
||
|
++loopKeepAliveCount_;
|
||
|
SCOPE_EXIT {
|
||
|
--loopKeepAliveCount_;
|
||
|
};
|
||
|
loopOnce();
|
||
|
}
|
||
|
|
||
|
// Implements the ScheduledExecutor interface
|
||
|
void scheduleAt(Func&& fn, TimePoint const& timeout) override;
|
||
|
|
||
|
// TimeoutManager
|
||
|
void attachTimeoutManager(
|
||
|
AsyncTimeout* obj,
|
||
|
TimeoutManager::InternalEnum internal) final;
|
||
|
|
||
|
void detachTimeoutManager(AsyncTimeout* obj) final;
|
||
|
|
||
|
bool scheduleTimeout(AsyncTimeout* obj, TimeoutManager::timeout_type timeout)
|
||
|
final;
|
||
|
|
||
|
void cancelTimeout(AsyncTimeout* obj) final;
|
||
|
|
||
|
bool isInTimeoutManagerThread() final {
|
||
|
return isInEventBaseThread();
|
||
|
}
|
||
|
|
||
|
// Returns a VirtualEventBase attached to this EventBase. Can be used to
|
||
|
// pass to APIs which expect VirtualEventBase. This VirtualEventBase will be
|
||
|
// destroyed together with the EventBase.
|
||
|
//
|
||
|
// Any number of VirtualEventBases instances may be independently constructed,
|
||
|
// which are backed by this EventBase. This method should be only used if you
|
||
|
// don't need to manage the life time of the VirtualEventBase used.
|
||
|
folly::VirtualEventBase& getVirtualEventBase();
|
||
|
|
||
|
/// Implements the IOExecutor interface
|
||
|
EventBase* getEventBase() override;
|
||
|
|
||
|
static std::unique_ptr<EventBaseBackendBase> getDefaultBackend();
|
||
|
|
||
|
protected:
|
||
|
bool keepAliveAcquire() override {
|
||
|
if (inRunningEventBaseThread()) {
|
||
|
loopKeepAliveCount_++;
|
||
|
} else {
|
||
|
loopKeepAliveCountAtomic_.fetch_add(1, std::memory_order_relaxed);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void keepAliveRelease() override {
|
||
|
if (!inRunningEventBaseThread()) {
|
||
|
return add([this] { loopKeepAliveCount_--; });
|
||
|
}
|
||
|
loopKeepAliveCount_--;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
folly::VirtualEventBase* tryGetVirtualEventBase();
|
||
|
|
||
|
void applyLoopKeepAlive();
|
||
|
|
||
|
ssize_t loopKeepAliveCount();
|
||
|
|
||
|
/*
|
||
|
* Helper function that tells us whether we have already handled
|
||
|
* some event/timeout/callback in this loop iteration.
|
||
|
*/
|
||
|
bool nothingHandledYet() const noexcept;
|
||
|
|
||
|
typedef LoopCallback::List LoopCallbackList;
|
||
|
class FunctionRunner;
|
||
|
|
||
|
bool loopBody(int flags = 0, bool ignoreKeepAlive = false);
|
||
|
|
||
|
// executes any callbacks queued by runInLoop(); returns false if none found
|
||
|
bool runLoopCallbacks();
|
||
|
|
||
|
void initNotificationQueue();
|
||
|
|
||
|
// Tick granularity to wheelTimer_
|
||
|
std::chrono::milliseconds intervalDuration_{
|
||
|
HHWheelTimer::DEFAULT_TICK_INTERVAL};
|
||
|
// should only be accessed through public getter
|
||
|
HHWheelTimer::UniquePtr wheelTimer_;
|
||
|
|
||
|
LoopCallbackList loopCallbacks_;
|
||
|
LoopCallbackList runBeforeLoopCallbacks_;
|
||
|
Synchronized<OnDestructionCallback::List> onDestructionCallbacks_;
|
||
|
|
||
|
// This will be null most of the time, but point to currentCallbacks
|
||
|
// if we are in the middle of running loop callbacks, such that
|
||
|
// runInLoop(..., true) will always run in the current loop
|
||
|
// iteration.
|
||
|
LoopCallbackList* runOnceCallbacks_;
|
||
|
|
||
|
// stop_ is set by terminateLoopSoon() and is used by the main loop
|
||
|
// to determine if it should exit
|
||
|
std::atomic<bool> stop_;
|
||
|
|
||
|
// The ID of the thread running the main loop.
|
||
|
// std::thread::id{} if loop is not running.
|
||
|
std::atomic<std::thread::id> loopThread_;
|
||
|
|
||
|
// A notification queue for runInEventBaseThread() to use
|
||
|
// to send function requests to the EventBase thread.
|
||
|
std::unique_ptr<NotificationQueue<Func>> queue_;
|
||
|
std::unique_ptr<FunctionRunner> fnRunner_;
|
||
|
ssize_t loopKeepAliveCount_{0};
|
||
|
std::atomic<ssize_t> loopKeepAliveCountAtomic_{0};
|
||
|
bool loopKeepAliveActive_{false};
|
||
|
|
||
|
// limit for latency in microseconds (0 disables)
|
||
|
std::chrono::microseconds maxLatency_;
|
||
|
|
||
|
// exponentially-smoothed average loop time for latency-limiting
|
||
|
SmoothLoopTime avgLoopTime_;
|
||
|
|
||
|
// smoothed loop time used to invoke latency callbacks; differs from
|
||
|
// avgLoopTime_ in that it's scaled down after triggering a callback
|
||
|
// to reduce spamminess
|
||
|
SmoothLoopTime maxLatencyLoopTime_;
|
||
|
|
||
|
// callback called when latency limit is exceeded
|
||
|
Func maxLatencyCob_;
|
||
|
|
||
|
// Enables/disables time measurements in loopBody(). if disabled, the
|
||
|
// following functionality that relies on time-measurement, will not
|
||
|
// be supported: avg loop time, observer and max latency.
|
||
|
const bool enableTimeMeasurement_;
|
||
|
|
||
|
// Wrap-around loop counter to detect beginning of each loop
|
||
|
std::size_t nextLoopCnt_;
|
||
|
std::size_t latestLoopCnt_;
|
||
|
std::chrono::steady_clock::time_point startWork_;
|
||
|
// Prevent undefined behavior from invoking event_base_loop() reentrantly.
|
||
|
// This is needed since many projects use libevent-1.4, which lacks commit
|
||
|
// b557b175c00dc462c1fce25f6e7dd67121d2c001 from
|
||
|
// https://github.com/libevent/libevent/.
|
||
|
bool invokingLoop_{false};
|
||
|
|
||
|
// Observer to export counters
|
||
|
std::shared_ptr<EventBaseObserver> observer_;
|
||
|
uint32_t observerSampleCount_;
|
||
|
|
||
|
// EventHandler's execution observer.
|
||
|
ExecutionObserver* executionObserver_;
|
||
|
|
||
|
// Name of the thread running this EventBase
|
||
|
std::string name_;
|
||
|
|
||
|
// see EventBaseLocal
|
||
|
friend class detail::EventBaseLocalBase;
|
||
|
template <typename T>
|
||
|
friend class EventBaseLocal;
|
||
|
std::unordered_map<std::size_t, std::shared_ptr<void>> localStorage_;
|
||
|
std::unordered_set<detail::EventBaseLocalBaseBase*> localStorageToDtor_;
|
||
|
|
||
|
folly::once_flag virtualEventBaseInitFlag_;
|
||
|
std::unique_ptr<VirtualEventBase> virtualEventBase_;
|
||
|
|
||
|
// pointer to underlying backend class doing the heavy lifting
|
||
|
std::unique_ptr<EventBaseBackendBase> evb_;
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
void EventBase::runInEventBaseThread(void (*fn)(T*), T* arg) noexcept {
|
||
|
return runInEventBaseThread([=] { fn(arg); });
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
void EventBase::runInEventBaseThreadAlwaysEnqueue(
|
||
|
void (*fn)(T*),
|
||
|
T* arg) noexcept {
|
||
|
return runInEventBaseThreadAlwaysEnqueue([=] { fn(arg); });
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
void EventBase::runInEventBaseThreadAndWait(void (*fn)(T*), T* arg) noexcept {
|
||
|
return runInEventBaseThreadAndWait([=] { fn(arg); });
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
void EventBase::runImmediatelyOrRunInEventBaseThreadAndWait(
|
||
|
void (*fn)(T*),
|
||
|
T* arg) noexcept {
|
||
|
return runImmediatelyOrRunInEventBaseThreadAndWait([=] { fn(arg); });
|
||
|
}
|
||
|
|
||
|
} // namespace folly
|