2023-12-31 17:10:04 +00:00
|
|
|
//SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
|
|
|
//SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
|
|
#include "scheduler.h"
|
|
|
|
|
2024-01-03 22:20:01 +00:00
|
|
|
const TM::Record::ID TM::Scheduler::none = 0;
|
|
|
|
|
2023-12-31 17:10:04 +00:00
|
|
|
TM::Scheduler::Scheduler (std::weak_ptr<Manager> manager):
|
|
|
|
queue(),
|
2024-01-03 22:20:01 +00:00
|
|
|
scheduled(),
|
2024-01-03 01:11:56 +00:00
|
|
|
manager(manager),
|
2023-12-31 17:10:04 +00:00
|
|
|
mutex(),
|
|
|
|
cond(),
|
|
|
|
thread(nullptr),
|
2024-01-03 22:20:01 +00:00
|
|
|
running(false),
|
|
|
|
idCounter(TM::Scheduler::none)
|
2023-12-31 17:10:04 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
TM::Scheduler::~Scheduler () {
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TM::Scheduler::start () {
|
|
|
|
std::unique_lock lock(mutex);
|
|
|
|
if (running)
|
|
|
|
return;
|
|
|
|
|
|
|
|
running = true;
|
|
|
|
thread = std::make_unique<std::thread>(&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<std::mutex> lock(mutex);
|
|
|
|
if (queue.empty()) {
|
|
|
|
cond.wait(lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Time currentTime = std::chrono::steady_clock::now();
|
|
|
|
while (!queue.empty()) {
|
2024-01-03 22:20:01 +00:00
|
|
|
Time nextScheduledTime = queue.top().time;
|
2023-12-31 17:10:04 +00:00
|
|
|
if (nextScheduledTime > currentTime) {
|
|
|
|
cond.wait_until(lock, nextScheduledTime);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-01-03 22:20:01 +00:00
|
|
|
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;
|
|
|
|
|
2023-12-31 17:10:04 +00:00
|
|
|
lock.unlock();
|
2024-01-03 01:11:56 +00:00
|
|
|
std::shared_ptr<Manager> mngr = manager.lock();
|
|
|
|
if (mngr)
|
2024-01-03 22:20:01 +00:00
|
|
|
mngr->schedule(std::make_unique<Function>(record.task));
|
2024-01-03 01:11:56 +00:00
|
|
|
|
2023-12-31 17:10:04 +00:00
|
|
|
lock.lock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-03 22:20:01 +00:00
|
|
|
TM::Record::ID TM::Scheduler::schedule (const Task& task, Delay delay) {
|
2023-12-31 17:10:04 +00:00
|
|
|
std::unique_lock lock(mutex);
|
|
|
|
Time time = std::chrono::steady_clock::now() + delay;
|
2024-01-03 22:20:01 +00:00
|
|
|
queue.emplace(++idCounter, task, time);
|
|
|
|
scheduled.emplace(idCounter);
|
2023-12-31 17:10:04 +00:00
|
|
|
|
|
|
|
lock.unlock();
|
|
|
|
cond.notify_one();
|
2024-01-03 22:20:01 +00:00
|
|
|
|
|
|
|
return idCounter;
|
2023-12-31 17:10:04 +00:00
|
|
|
}
|
2024-01-03 22:20:01 +00:00
|
|
|
|
|
|
|
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
|