神刀安全网

Cosche, a dependency-based coroutine scheduler (C++)

cosche

Disclaimer: This is a zeta version using a greek modulo versioning (the next iteration may be an alpha ;-)) ! I wouldn’t recommend using it for anything else than a toy project. Furthermore, this is my first project in this realm of concurrency so I have probably done some huge mistakes and will gladly accept any reviews and feedbacks.

What is this ?

Cosche is a small dependency-based coroutine scheduler able to register std::future(s) in its main loop to interact with threads and other asynchronous I/O libraries. It is based onboost::context written by Oliver Kowalke and I got the idea of building this by watching an amazing conference from Amaury Sechet on theStupid D Compiler. So thanks to both of you ! :)

Features and limitations

:heavy_plus_sign: very small and easy to use

:heavy_plus_sign: threads and async IO interactions via futures

:heavy_plus_sign: support arbitrary dependency graph and detect cycles

:heavy_minus_sign: not optimized for speed

:heavy_minus_sign: each scheduler is bound to a unique thread

Show me some code !

cosche::Scheduler scheduler;  //scheduler.reserveTasks<void>(2);  auto ping = scheduler.getNewTask<void>(); auto pong = scheduler.getNewTask<void>();  ping (     [&]()     {         std::cout << "ping" << std::endl;         pong.detach(ping);         ping.attach(pong);         std::cout << "ping" << std::endl;         ping.release();          // DO NOT use as it does not register         // an edge in the dependency graph         ping.wait(pong.getFuture());          ping.waitFor(std::chrono::seconds(1),             std::async(std::launch::async,                 []()                 {                    std::this_thread::sleep_for(std::chrono::seconds(2));                    std::cout << "task" << std::endl;                 }             )         );          std::cout << "ping" << std::endl;     } );  pong (     [&]()     {         std::cout << "pong" << std::endl;         //pong.throwing(std::runtime_error("throw !"));         ping.detach(pong);         pong.attach(ping);          std::cout << "pong" << std::endl;     } );  ping.onCycle([]() { std::cerr << "ping belongs to a cycle" << std::endl; }); pong.onCycle([]() { std::cerr << "pong belongs to a cycle" << std::endl; });  pong.attach(ping);  try {     scheduler.run(); } catch (const cosche::Cycle& cycle) {     std::cerr << "The scheduler ended on a cycle !" << std::endl;      cycle(); }

The output of the obove program will be

ping pong ping pong [wait for 1 second] ping [wait for 1 second] task 

Overview

namespace cosche {     struct Scheduler     {         template <class Rt> Task<Rt>& Scheduler::getNewTask();         template <class Rt> void reserveTasks(std::size_t);         void Scheduler::run();     }; }

The scheduler allows to create a task with a given return type ( getNewTask ) reserve them for faster creation ( reserveTasks ) and launch the execution of registered tasks ( run ).

namespace cosche {     template <class Rt>     struct Task     {         virtual std::size_t id() const;         Scheduler& scheduler() const;         template <class Fn, class... Args> void operator()(Fn&& fn, Args&&... args);         void attach(AbstractTask& task);         void detach(AbstractTask& task);         void release();         template <class T> T wait(std::future<T>&& future);         template <class Rep, class Period, class T> std::future<T>& waitFor(const std::chrono::duration<Rep,Period>& timeoutDuration, std::future<T>&& future);         template <class Fn, class... Args> void onCycle(Fn&& fn, Args&&... args);         template <class E> void throwing(const E& e);         std::future<Rt> getFuture() { return _task->get_future(); }     }; }

For a given task you can get an id and the scheduler it was registered in. The content of a task is defined with operator() . You can attach and detach them to other tasks in the graph of their common scheduler. A task can detach all the tasks that attached it via release . A task cam wait or waitFor a given time on a future. You can defined a task to be executed in case it is part of a cycle ( oncycle ). And throw an exception outside of the scheduler run ( throwing ). You can catch normal throw with getFuture or synchronize with tasks defined within an other scheduler.

  • TODOs

    • add exemples/tests/banchmarks
    • think about what to add
    • fill this README

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Cosche, a dependency-based coroutine scheduler (C++)

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址