mirror of
https://gitlab.com/niansa/cosched.git
synced 2025-03-06 20:53:26 +01:00
Initial commit
This commit is contained in:
commit
dbc07373d4
7 changed files with 319 additions and 0 deletions
76
.gitignore
vendored
Normal file
76
.gitignore
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# Qt Creator
|
||||||
|
CMakeLists.txt.user*
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "libasync"]
|
||||||
|
path = libasync
|
||||||
|
url = https://gitlab.com/niansa/libasync.git
|
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
project(cosched LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
add_subdirectory(libasync)
|
||||||
|
|
||||||
|
add_library(cosched STATIC scheduler.cpp include/scheduler.hpp)
|
||||||
|
target_link_libraries(cosched PUBLIC async)
|
||||||
|
target_include_directories(cosched PUBLIC include/)
|
||||||
|
|
||||||
|
install(TARGETS cosched
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
157
include/scheduler.hpp
Normal file
157
include/scheduler.hpp
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
#ifndef _SCHEDULER_HPP
|
||||||
|
#define _SCHEDULER_HPP
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
#include <limits>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <async/result.hpp>
|
||||||
|
#include <async/recurring-event.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace CoSched {
|
||||||
|
using Priority = uint8_t;
|
||||||
|
enum {
|
||||||
|
PRIO_LOWEST = -99,
|
||||||
|
PRIO_LOW = -20,
|
||||||
|
PRIO_NORMAL = 0,
|
||||||
|
PRIO_HIGH = 20,
|
||||||
|
PRIO_HIGHEST = 99,
|
||||||
|
PRIO_REALTIME = PRIO_HIGHEST
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TaskState {
|
||||||
|
running,
|
||||||
|
sleeping,
|
||||||
|
starting,
|
||||||
|
stopping
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Task {
|
||||||
|
friend class Scheduler;
|
||||||
|
|
||||||
|
class Scheduler *scheduler;
|
||||||
|
async::recurring_event resume;
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point stopped_at;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
Priority priority = PRIO_NORMAL;
|
||||||
|
TaskState state = TaskState::starting;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Task(Scheduler *scheduler, const std::string& name)
|
||||||
|
: scheduler(scheduler), name(name) {}
|
||||||
|
Task(const Task&) = delete;
|
||||||
|
Task(Task&&) = delete;
|
||||||
|
|
||||||
|
const std::string& get_name() const {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
void set_name(const std::string& value) {
|
||||||
|
name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Priority get_priority() const {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
void set_priority(Priority value) {
|
||||||
|
priority = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskState get_state() const {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheduler& get_scheduler() const {
|
||||||
|
return *scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
async::result<void> yield();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TaskPtr {
|
||||||
|
unsigned *ref_count;
|
||||||
|
Task *task;
|
||||||
|
|
||||||
|
void finalize();
|
||||||
|
|
||||||
|
public:
|
||||||
|
TaskPtr(Task *task) : task(task) {
|
||||||
|
ref_count = new unsigned(1);
|
||||||
|
}
|
||||||
|
~TaskPtr() {
|
||||||
|
if (!task) return;
|
||||||
|
if (--*ref_count == 0) {
|
||||||
|
delete ref_count;
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TaskPtr(const TaskPtr& o) : ref_count(o.ref_count), task(o.task) {
|
||||||
|
++*ref_count;
|
||||||
|
}
|
||||||
|
TaskPtr(TaskPtr&& o) : ref_count(o.ref_count), task(o.task) {
|
||||||
|
o.task = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator ->() {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Scheduler {
|
||||||
|
friend class Task;
|
||||||
|
friend class TaskPtr;
|
||||||
|
|
||||||
|
std::mutex tasks_mutex;
|
||||||
|
std::vector<std::unique_ptr<Task>> tasks;
|
||||||
|
|
||||||
|
async::result<void> yield(Task *task);
|
||||||
|
|
||||||
|
void delete_task(Task *task) {
|
||||||
|
std::scoped_lock L(tasks_mutex);
|
||||||
|
tasks.erase(std::find_if(tasks.begin(), tasks.end(), [task] (const auto& o) {return o.get() == task;}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Task *get_next_task();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Scheduler() {}
|
||||||
|
Scheduler(const Scheduler&) = delete;
|
||||||
|
Scheduler(Scheduler&&) = delete;
|
||||||
|
|
||||||
|
const auto& get_tasks() const {
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPtr create_task(const std::string& name) {
|
||||||
|
std::scoped_lock L(tasks_mutex);
|
||||||
|
return TaskPtr(tasks.emplace_back(std::make_unique<Task>(this, name)).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
while (!tasks.empty()) {
|
||||||
|
// Get next task
|
||||||
|
auto next_task = get_next_task();
|
||||||
|
|
||||||
|
// Resume task if any
|
||||||
|
if (next_task) next_task->resume.raise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline async::result<void> Task::yield() {
|
||||||
|
co_await scheduler->yield(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TaskPtr::finalize() {
|
||||||
|
task->get_scheduler().delete_task(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // _SCHEDULER_HPP
|
1
libasync
Submodule
1
libasync
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 721195383b9005006826856fe84c5eabdf350882
|
42
scheduler.cpp
Normal file
42
scheduler.cpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async::result<void> CoSched::Scheduler::yield(Task *task) {
|
||||||
|
task->state = TaskState::sleeping;
|
||||||
|
task->stopped_at = std::chrono::system_clock::now();
|
||||||
|
co_await task->resume.async_wait();
|
||||||
|
task->state = TaskState::running;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoSched::Task *CoSched::Scheduler::get_next_task() {
|
||||||
|
std::scoped_lock L(tasks_mutex);
|
||||||
|
|
||||||
|
// Get tasks with highest priority
|
||||||
|
std::vector<Task*> max_prio_tasks;
|
||||||
|
Priority max_prio = std::numeric_limits<Priority>::min();
|
||||||
|
for (auto& task : tasks) {
|
||||||
|
// Filter tasks that aren't sleeping
|
||||||
|
if (task->state != TaskState::sleeping) continue;
|
||||||
|
// Update max priority
|
||||||
|
if (task->priority > max_prio) {
|
||||||
|
max_prio = task->priority;
|
||||||
|
max_prio_tasks.clear();
|
||||||
|
}
|
||||||
|
// Add task if matching
|
||||||
|
if (task->priority == max_prio) {
|
||||||
|
max_prio_tasks.push_back(task.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get least recently stopped task
|
||||||
|
Task *next_task = nullptr;
|
||||||
|
for (auto task : max_prio_tasks) {
|
||||||
|
if (!next_task || task->stopped_at < next_task->stopped_at) {
|
||||||
|
next_task = task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return next task;
|
||||||
|
return next_task;
|
||||||
|
}
|
25
test.cpp
Normal file
25
test.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async::result<void> test_task(CoSched::TaskPtr t) {
|
||||||
|
for (unsigned x = 100; x != 0; x--) {
|
||||||
|
std::cout << t->get_name() << ": " << x << '\n';
|
||||||
|
co_await t->yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main () {
|
||||||
|
CoSched::Scheduler scheduler;
|
||||||
|
for (const auto& name : {"A", "B", "C", "D", "E", "F"}) {
|
||||||
|
auto task = scheduler.create_task(name);
|
||||||
|
async::detach(test_task(task));
|
||||||
|
if (task->get_name() == "B" || task->get_name() == "D") {
|
||||||
|
task->set_priority(CoSched::PRIO_HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scheduler.run();
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue