// This file is originally from Moduo -> Trantor - EventLoop.h // Copyright (c) 2016-2021, Tao An. All rights reserved. // Copyright (c) 2010, Shuo Chen. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Tao An nor the names of other contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include "core/containers/lock_free_queue.h" #include "core/math/date.h" #include #include #include #include #include #include #include #include class Poller; class TimerQueue; class Channel; using ChannelList = std::vector; using Func = std::function; using TimerId = uint64_t; enum { InvalidTimerId = 0 }; /** * @brief As the name implies, this class represents an event loop that runs in * a perticular thread. The event loop can handle network I/O events and timers * in asynchronous mode. * @note An event loop object always belongs to a separate thread, and there is * one event loop object at most in a thread. We can call an event loop object * the event loop of the thread it belongs to, or call that thread the thread of * the event loop. */ class EventLoop { protected: EventLoop(const EventLoop &) = delete; EventLoop &operator=(const EventLoop &) = delete; // some uncopyable classes maybe support move constructor.... EventLoop(EventLoop &&) noexcept(true) = default; EventLoop &operator=(EventLoop &&) noexcept(true) = default; public: EventLoop(); ~EventLoop(); /** * @brief Run the event loop. This method will be blocked until the event * loop exits. * */ void loop(); /** * @brief Let the event loop quit. * */ void quit(); /** * @brief Assertion that the current thread is the thread to which the event * loop belongs. If the assertion fails, the program aborts. */ void assertInLoopThread() { if (!isInLoopThread()) { abortNotInLoopThread(); } }; #ifdef __linux__ /** * @brief Make the timer queue works after calling the fork() function. * */ void resetTimerQueue(); #endif /** * @brief Make the event loop works after calling the fork() function. * */ void resetAfterFork(); /** * @brief Return true if the current thread is the thread to which the event * loop belongs. * * @return true * @return false */ bool isInLoopThread() const { return threadId_ == std::this_thread::get_id(); }; /** * @brief Get the event loop of the current thread. Return nullptr if there * is no event loop in the current thread. * * @return EventLoop* */ static EventLoop *getEventLoopOfCurrentThread(); /** * @brief Run the function f in the thread of the event loop. * * @param f * @note If the current thread is the thread of the event loop, the function * f is executed directly before the method exiting. */ void runInLoop(const Func &f); void runInLoop(Func &&f); /** * @brief Run the function f in the thread of the event loop. * * @param f * @note The difference between this method and the runInLoop() method is * that the function f is executed after the method exiting no matter if the * current thread is the thread of the event loop. */ void queueInLoop(const Func &f); void queueInLoop(Func &&f); /** * @brief Run a function at a time point. * * @param time The time to run the function. * @param cb The function to run. * @return TimerId The ID of the timer. */ TimerId runAt(const Date &time, const Func &cb); TimerId runAt(const Date &time, Func &&cb); /** * @brief Run a function after a period of time. * * @param delay Represent the period of time in seconds. * @param cb The function to run. * @return TimerId The ID of the timer. */ TimerId runAfter(double delay, const Func &cb); TimerId runAfter(double delay, Func &&cb); /** * @brief Run a function after a period of time. * @note Users could use chrono literals to represent a time duration * For example: * @code runAfter(5s, task); runAfter(10min, task); @endcode */ TimerId runAfter(const std::chrono::duration &delay, const Func &cb) { return runAfter(delay.count(), cb); } TimerId runAfter(const std::chrono::duration &delay, Func &&cb) { return runAfter(delay.count(), std::move(cb)); } /** * @brief Repeatedly run a function every period of time. * * @param interval The duration in seconds. * @param cb The function to run. * @return TimerId The ID of the timer. */ TimerId runEvery(double interval, const Func &cb); TimerId runEvery(double interval, Func &&cb); /** * @brief Repeatedly run a function every period of time. * Users could use chrono literals to represent a time duration * For example: * @code runEvery(5s, task); runEvery(10min, task); runEvery(0.1h, task); @endcode */ TimerId runEvery(const std::chrono::duration &interval, const Func &cb) { return runEvery(interval.count(), cb); } TimerId runEvery(const std::chrono::duration &interval, Func &&cb) { return runEvery(interval.count(), std::move(cb)); } /** * @brief Invalidate the timer identified by the given ID. * * @param id The ID of the timer. */ void invalidateTimer(TimerId id); /** * @brief Move the EventLoop to the current thread, this method must be * called before the loop is running. * */ void moveToCurrentThread(); /** * @brief Update channel status. This method is usually used internally. * * @param chl */ void updateChannel(Channel *chl); /** * @brief Remove a channel from the event loop. This method is usually used * internally. * * @param chl */ void removeChannel(Channel *chl); /** * @brief Return the index of the event loop. * * @return size_t */ size_t index() { return index_; } /** * @brief Set the index of the event loop. * * @param index */ void setIndex(size_t index) { index_ = index; } /** * @brief Return true if the event loop is running. * * @return true * @return false */ bool isRunning() { return looping_ && (!quit_); } /** * @brief Check if the event loop is calling a function. * * @return true * @return false */ bool isCallingFunctions() { return callingFuncs_; } /** * @brief Run functions when the event loop quits * * @param cb the function to run * @note the function runs on the thread that quits the EventLoop */ void runOnQuit(Func &&cb); void runOnQuit(const Func &cb); private: void abortNotInLoopThread(); void wakeup(); void wakeupRead(); bool looping_; std::thread::id threadId_; bool quit_; std::unique_ptr poller_; ChannelList activeChannels_; Channel *currentActiveChannel_; bool eventHandling_; MpscQueue funcs_; std::unique_ptr timerQueue_; MpscQueue funcsOnQuit_; bool callingFuncs_{ false }; #ifdef __linux__ int wakeupFd_; std::unique_ptr wakeupChannelPtr_; #elif defined _WIN32 #else int wakeupFd_[2]; std::unique_ptr wakeupChannelPtr_; #endif void doRunInLoopFuncs(); #ifdef _WIN32 size_t index_{ size_t(-1) }; #else size_t index_{ std::numeric_limits::max() }; #endif EventLoop **threadLocalLoopPtr_; };