//SPDX-FileCopyrightText: 2023 Yury Gubich //SPDX-License-Identifier: GPL-3.0-or-later #include "scheduler.h" const TM::Record::ID TM::Scheduler::none = 0; TM::Scheduler::Scheduler (std::weak_ptr manager): queue(), scheduled(), manager(manager), mutex(), cond(), thread(nullptr), running(false), idCounter(TM::Scheduler::none) {} TM::Scheduler::~Scheduler () { stop(); } void TM::Scheduler::start () { std::unique_lock lock(mutex); if (running) return; running = true; thread = std::make_unique(&Scheduler::loop, this); } void TM::Scheduler::stop () { std::unique_lock lock(mutex); if (!running) return; running = false; lock.unlock(); cond.notify_all(); thread->join(); lock.lock(); thread.reset(); } void TM::Scheduler::loop () { while (running) { std::unique_lock lock(mutex); if (queue.empty()) { cond.wait(lock); continue; } Time currentTime = std::chrono::steady_clock::now(); while (!queue.empty()) { Time nextScheduledTime = queue.top().time; if (nextScheduledTime > currentTime) { cond.wait_until(lock, nextScheduledTime); break; } Record record = queue.pop(); std::size_t count = scheduled.erase(record.id); if (count == 0) //it means this record has been cancelled, no need to execute it continue; lock.unlock(); std::shared_ptr mngr = manager.lock(); if (mngr) mngr->schedule(std::make_unique(record.task)); lock.lock(); } } } TM::Record::ID TM::Scheduler::schedule (const Task& task, Delay delay) { std::unique_lock lock(mutex); Time time = std::chrono::steady_clock::now() + delay; queue.emplace(++idCounter, task, time); scheduled.emplace(idCounter); lock.unlock(); cond.notify_one(); return idCounter; } bool TM::Scheduler::cancel (Record::ID id) { return scheduled.erase(id) != 0; //not to mess with the queue, here we just mark it as not scheduled } //and when the time comes it will be just discarded