#include "asio.hpp" #include "Global.h" #include "Plot_p.h" #include #include namespace YSG { struct RenderSchedulerPrivate { asio::io_context mIoContext; std::unique_ptr> mWorkGuard; std::unique_ptr mCpuPool; std::thread mSchedulerThread; std::thread::id mSchedulerThreadId; QList mPlots; std::atomic_bool mRunning{false}; std::atomic_bool mCpuJoined{true}; }; RenderScheduler::RenderScheduler() : d(std::make_unique()) { QObject::connect(QApplication::instance(), &QApplication::aboutToQuit, [this]() { shutdown(); }); } RenderScheduler::~RenderScheduler() { shutdown(); } void RenderScheduler::start() { if(d->mRunning.exchange(true, std::memory_order_acq_rel)) return; d->mIoContext.restart(); d->mWorkGuard = std::make_unique>(d->mIoContext.get_executor()); unsigned cpuCount = std::max(1u, std::thread::hardware_concurrency() > 1 ? std::thread::hardware_concurrency() - 1 : 1u); d->mCpuPool = std::make_unique(cpuCount); d->mCpuJoined.store(false, std::memory_order_release); d->mSchedulerThread = std::thread([this]() { d->mSchedulerThreadId = std::this_thread::get_id(); for(Plot* plot : d->mPlots) { initializePlotOnScheduler(plot); } d->mIoContext.run(); d->mRunning.store(false, std::memory_order_release); }); } void RenderScheduler::shutdown() { if(!d->mRunning.load(std::memory_order_acquire)) { joinCpuPool(); if(d->mSchedulerThread.joinable() && d->mSchedulerThread.get_id() != std::this_thread::get_id()) d->mSchedulerThread.join(); d->mCpuPool.reset(); d->mWorkGuard.reset(); return; } if(isSchedulerThread()) { shutdownOnScheduler(); d->mWorkGuard.reset(); joinCpuPool(); return; } postAndWait([this]() { shutdownOnScheduler(); }); joinCpuPool(); post([this]() { d->mWorkGuard.reset(); }); if(d->mSchedulerThread.joinable()) d->mSchedulerThread.join(); d->mCpuPool.reset(); } void RenderScheduler::post(std::function task) { asio::post(d->mIoContext, std::move(task)); } void RenderScheduler::postCpu(std::function task) { if(d->mCpuPool) { asio::post(*d->mCpuPool, std::move(task)); } else { post(std::move(task)); } } void RenderScheduler::registerPlot(Plot* plot) { if(d->mRunning.load(std::memory_order_acquire) && !isSchedulerThread()) { postAndWait([this, plot]() { registerPlotOnScheduler(plot); }); return; } registerPlotOnScheduler(plot); } void RenderScheduler::removePlot(Plot* plot) { if(d->mRunning.load(std::memory_order_acquire) && !isSchedulerThread()) { postAndWait([this, plot]() { removePlotOnScheduler(plot); }); return; } removePlotOnScheduler(plot); } bool RenderScheduler::isSchedulerThread() const { return std::this_thread::get_id() == d->mSchedulerThreadId; } bool RenderScheduler::isRunning() const { return d->mRunning.load(std::memory_order_acquire); } asio::io_context& RenderScheduler::ioContext() { return d->mIoContext; } void RenderScheduler::joinCpuPool() { if(d->mCpuPool && !d->mCpuJoined.exchange(true, std::memory_order_acq_rel)) d->mCpuPool->join(); } void RenderScheduler::postAndWait(std::function task) { if(isSchedulerThread() || !d->mRunning.load(std::memory_order_acquire)) { task(); return; } std::mutex mutex; std::condition_variable cv; bool done = false; post([&task, &mutex, &cv, &done]() { task(); { std::lock_guard lock(mutex); done = true; } cv.notify_one(); }); std::unique_lock lock(mutex); cv.wait(lock, [&done]() { return done; }); } void RenderScheduler::registerPlotOnScheduler(Plot* plot) { if(!d->mPlots.contains(plot)) d->mPlots.append(plot); if(d->mRunning.load(std::memory_order_acquire)) initializePlotOnScheduler(plot); } void RenderScheduler::removePlotOnScheduler(Plot* plot) { d->mPlots.removeAll(plot); PlotPrivate* data = plot->d; data->mRenderEnabled.store(false, std::memory_order_release); if(data->mRenderTimer) { data->mRenderTimer->cancel(); data->mRenderTimer.reset(); } } void RenderScheduler::shutdownOnScheduler() { for(Plot* plot : d->mPlots) { PlotPrivate* data = plot->d; data->mDestroying.store(true, std::memory_order_release); data->mRenderEnabled.store(false, std::memory_order_release); if(data->mRenderTimer) { data->mRenderTimer->cancel(); data->mRenderTimer.reset(); } } d->mPlots.clear(); } void RenderScheduler::initializePlotOnScheduler(Plot* plot) { PlotPrivate* data = plot->d; if(!data->mRenderTimer) data->mRenderTimer = std::make_unique(d->mIoContext); data->mRenderSize = QSize(data->mPendingRenderWidth.load(std::memory_order_acquire), data->mPendingRenderHeight.load(std::memory_order_acquire)); if(plot->isVisible()) data->mRenderEnabled.store(true, std::memory_order_release); if(data->mRenderEnabled.load(std::memory_order_acquire)) plot->scheduleRenderTimer(); plot->submitRender(); } RenderScheduler& Global::renderScheduler() { return mRenderScheduler; } void Global::startRenderScheduler() { mRenderScheduler.start(); } }