/** * @file coro_scheduler.h * @author Nat Goodspeed * @date 2024-08-05 * @brief Custom scheduler for viewer's Boost.Fibers (aka coroutines) * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Copyright (c) 2024, Linden Research, Inc. * $/LicenseInfo$ */ #if ! defined(LL_CORO_SCHEDULER_H) #define LL_CORO_SCHEDULER_H #include "workqueue.h" #include #include /** * llcoro::scheduler is specifically intended for the viewer's main thread. * Its role is to ensure that the main coroutine, responsible for UI * operations and coordinating everything else, doesn't get starved by * secondary coroutines -- however many of those there might be. * * The simple boost::fibers::algo::round_robin scheduler could result in * arbitrary time lag between resumptions of the main coroutine. Of course * every well-behaved viewer coroutine must be coded to yield before too much * real time has elapsed, but sheer volume of secondary coroutines could still * consume unreasonable real time before cycling back to the main coroutine. */ namespace llcoro { class scheduler: public boost::fibers::algo::round_robin { using super = boost::fibers::algo::round_robin; public: // If the main fiber is ready, and it's been at least this long since the // main fiber last ran, jump the main fiber to the head of the queue. static const F32 DEFAULT_TIMESLICE; scheduler(); void awakened( boost::fibers::context*) noexcept override; boost::fibers::context* pick_next() noexcept override; static void use(); private: // This is the fiber::id of the main fiber. We use this to discover // whether the fiber passed to awakened() is in fact the main fiber. boost::fibers::fiber::id mMainID; // This context* is nullptr until awakened() notices that the main fiber // has become ready, at which point it contains the main fiber's context*. boost::fibers::context* mMainCtx{}; // Set when pick_next() returns the main fiber. bool mMainRunning{ false }; // If it's been at least this long since the last time the main fiber got // control, jump it to the head of the queue. F32 mTimeslice{ DEFAULT_TIMESLICE }; // Timestamp as of the last time we suspended the main fiber. F32 mMainLast{ 0 }; // Timestamp of start time F32 mStart{ 0 }; // count context switches U64 mSwitches{ 0 }; // WorkQueue for deferred logging LL::WorkQueue::weak_t mQueue; }; } // namespace llcoro #endif /* ! defined(LL_CORO_SCHEDULER_H) */