/*----------------------------------------------------------------------------*/ /* Copyright (c) 2018 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ #ifndef WPIUTIL_WPI_UV_LOOP_H_ #define WPIUTIL_WPI_UV_LOOP_H_ #include #include #include #include #include #include #include #include "wpi/Signal.h" #include "wpi/uv/Error.h" namespace wpi { namespace uv { class Handle; /** * Event loop. * * The event loop is the central part of uv functionality. It takes care of * polling for I/O and scheduling signals to be generated based on different * sources of events. * * The event loop is not moveable, copyable, or directly constructible. Use * Create() to create an event loop, or GetDefault() to get the default loop * if you know your program will only have a single one. */ class Loop final : public std::enable_shared_from_this { struct private_init {}; public: using Time = std::chrono::duration; enum Mode { kDefault = UV_RUN_DEFAULT, kOnce = UV_RUN_ONCE, kNoWait = UV_RUN_NOWAIT }; explicit Loop(const private_init&) noexcept; Loop(const Loop&) = delete; Loop& operator=(const Loop&) = delete; Loop(Loop&& oth) = delete; Loop& operator=(Loop&& oth) = delete; ~Loop() noexcept; /** * Create a new event loop. The created loop is not the default event loop. * * @return The newly created loop. May return nullptr if a failure occurs. */ static std::shared_ptr Create(); /** * Create the default event loop. Only use this event loop if a single loop * is needed for the entire application. * * @return The newly created loop. May return nullptr if a failure occurs. */ static std::shared_ptr GetDefault(); /** * Release all internal loop resources. * * Call this function only when the loop has finished executing and all open * handles and requests have been closed, or the loop will emit an error. * * error() will be emitted in case of errors. */ void Close(); /** * Run the event loop. * * Available modes are: * * * `Loop::kDefault`: Run the event loop until there are no * active and referenced handles or requests. * * `Loop::kOnce`: Run a single event loop iteration. Note that this * function blocksif there are no pending callbacks. * * `Loop::kNoWait`: Run a single event loop iteration, but don't block * if there are no pending callbacks. * * @return True when done, false in all other cases. */ bool Run(Mode mode = kDefault) { m_tid = std::this_thread::get_id(); int rv = uv_run(m_loop, static_cast(static_cast(mode))); m_tid = std::thread::id{}; return rv == 0; } /** * Check if there are active resources. * * @return True if there are active resources in the loop. */ bool IsAlive() const noexcept { return uv_loop_alive(m_loop) != 0; } /** * Stop the event loop. * * This will cause Run() to end as soon as possible. * This will happen not sooner than the next loop iteration. * If this function was called before blocking for I/O, the loop won’t block * for I/O on this iteration. */ void Stop() noexcept { uv_stop(m_loop); } /** * Get backend file descriptor. * * Only kqueue, epoll and event ports are supported. * This can be used in conjunction with `run(Loop::kNoWait)` to poll * in one thread and run the event loop’s callbacks in another. * * @return The backend file descriptor. */ int GetDescriptor() const noexcept { return uv_backend_fd(m_loop); } /** * Get the poll timeout. * * @return A `std::pair` composed of a boolean value that is true in case of * valid timeout, false otherwise, and the timeout * (`std::chrono::duration`). */ std::pair GetTimeout() const noexcept { auto to = uv_backend_timeout(m_loop); return std::make_pair(to == -1, Time{to}); } /** * Return the current timestamp in milliseconds. * * The timestamp is cached at the start of the event loop tick. * The timestamp increases monotonically from some arbitrary point in * time. * Don’t make assumptions about the starting point, you will only get * disappointed. * * @return The current timestamp in milliseconds (actual type is * `std::chrono::duration`). */ Time Now() const noexcept { return Time{uv_now(m_loop)}; } /** * Update the event loop’s concept of _now_. * * The current time is cached at the start of the event loop tick in order * to reduce the number of time-related system calls. * You won’t normally need to call this function unless you have callbacks * that block the event loop for longer periods of time, where _longer_ is * somewhat subjective but probably on the order of a millisecond or more. */ void UpdateTime() noexcept { uv_update_time(m_loop); } /** * Walk the list of handles. * * The callback will be executed once for each handle that is still active. * * @param callback A function to be invoked once for each active handle. */ void Walk(std::function callback); /** * Reinitialize any kernel state necessary in the child process after * a fork(2) system call. * * Previously started watchers will continue to be started in the child * process. * * It is necessary to explicitly call this function on every event loop * created in the parent process that you plan to continue to use in the * child, including the default loop (even if you don’t continue to use it * in the parent). This function must be called before calling any API * function using the loop in the child. Failure to do so will result in * undefined behaviour, possibly including duplicate events delivered to * both parent and child or aborting the child process. * * When possible, it is preferred to create a new loop in the child process * instead of reusing a loop created in the parent. New loops created in the * child process after the fork should not use this function. * * Note that this function is not implemented on Windows. * Note also that this function is experimental in `libuv`. It may contain * bugs, and is subject to change or removal. API and ABI stability is not * guaranteed. * * error() will be emitted in case of errors. */ void Fork(); /** * Get the underlying event loop data structure. * * @return The underlying event loop data structure. */ uv_loop_t* GetRaw() const noexcept { return m_loop; } /** * Gets user-defined data. * @return User-defined data if any, nullptr otherwise. */ template std::shared_ptr GetData() const { return std::static_pointer_cast(m_data); } /** * Sets user-defined data. * @param data User-defined arbitrary data. */ void SetData(std::shared_ptr data) { m_data = std::move(data); } /** * Get the thread id of the loop thread. If the loop is not currently * running, returns default-constructed thread id. */ std::thread::id GetThreadId() const { return m_tid; } /** * Error signal */ sig::Signal error; /** * Reports error. * @param err Error code */ void ReportError(int err) { error(Error(err)); } private: std::shared_ptr m_data; uv_loop_t* m_loop; uv_loop_t m_loopStruct; std::atomic m_tid; }; } // namespace uv } // namespace wpi #endif // WPIUTIL_WPI_UV_LOOP_H_