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