A header-only C++ library for task concurrency

Overview

transwarp

Gitter Travis Appveyor

Doxygen documentation

transwarp is a header-only C++ library for task concurrency. It allows you to easily create a graph of tasks where every task can be executed asynchronously. transwarp is written in C++17 and only depends on the standard library. Just copy src/transwarp.h to your project and off you go! Tested with GCC, Clang, ICC, and Visual Studio.

C++11 support can be enabled by defining TRANSWARP_CPP11 at compile time.

Important: Only use tagged releases of transwarp in production code!

Table of contents

Example

This example creates three tasks and connects them with each other to form a two-level graph. The tasks are then scheduled twice for computation while using 4 threads.

#include <fstream>
#include <iostream>
#include "transwarp.h"

namespace tw = transwarp;

int main() {
    double x = 0;
    int y = 0;

    // Building the task graph
    auto parent1 = tw::make_task(tw::root, [&x]{ return 13.3 + x; })->named("something");
    auto parent2 = tw::make_task(tw::root, [&y]{ return 42 + y; })->named("something else");
    auto child = tw::make_task(tw::consume, [](double a, int b) { return a + b;
                                            }, parent1, parent2)->named("adder");

    tw::parallel executor{4};  // Parallel execution with 4 threads

    child->schedule_all(executor);  // Schedules all tasks for execution
    std::cout << "result = " << child->get() << std::endl;  // result = 55.3

    // Modifying data input
    x += 2.5;
    y += 1;

    child->schedule_all(executor);  // Re-schedules all tasks for execution
    std::cout << "result = " << child->get() << std::endl;  // result = 58.8

    // Creating a dot-style graph for visualization
    std::ofstream{"basic_with_three_tasks.dot"} << tw::to_string(child->edges());
}

The resulting graph of this example looks like this:

graph

Every bubble represents a task and every arrow an edge between two tasks. The first line within a bubble is the task name. The second line denotes the task type followed by the task id and the task level in the graph.

API doc

This is a brief API doc of transwarp. For more details check out the doxygen documentation and the transwarp examples.

In the following we will use tw as a namespace alias for transwarp.

Creating tasks

transwarp supports seven different task types:

root, // The task has no parents
accept, // The task's functor accepts all parent futures
accept_any, // The task's functor accepts the first parent future that becomes ready
consume, // The task's functor consumes all parent results
consume_any, // The task's functor consumes the first parent result that becomes ready
wait, // The task's functor takes no arguments but waits for all parents to finish
wait_any, // The task's functor takes no arguments but waits for the first parent to finish

The task type is passed as the first parameter to make_task, e.g., to create a consume task simply do this:

auto task = tw::make_task(tw::consume, functor, parent1, parent2);

where functor denotes some callable and parent1/2 the parent tasks.

The functor as passed to make_task needs to fulfill certain requirements based on the task type and the given parents:

root: A task at the root (top) of the graph. This task gets executed first. A functor to a root task cannot have any parameters since this task does not have parent tasks, e.g.:

auto task = tw::make_task(tw::root, []{ return 42; });

Another way of defining aroot task is a value task which can be created as:

auto task = tw::make_value_task(42);  

A value task doesn't require scheduling and always returns the same value or exception.

accept: This task is required to have at least one parent. It accepts the resulting parent futures as they are without unwrapping. Hence, the child can decide how to proceed since a call to get() can potentially throw an exception. Here's an example:

auto task = tw::make_task(tw::accept, [](auto f1, auto f2) { return f1.get() + f2.get(); }, parent1, parent2);

accept_any: This task is required to have at least one parent but its functor takes exactly one future, namely the future of the parent that first finishes. All other parents are abandoned and canceled. Here's an example:

auto task = tw::make_task(tw::accept_any, [](auto f1) { return f1.get(); }, parent1, parent2);

Note that canceling only works for already running tasks when the functor is sub-classed from transwarp::functor.

consume: This task follows the same rules as accept with the difference that the resulting parent futures are unwrapped (have get() called on them). The results are then passed to the child, hence, consumed by the child task. The child task will not be invoked if any parent throws an exception. For example:

auto task = tw::make_task(tw::consume, [](int x, int y) { return x + y; }, parent1, parent2);

consume_any: This task follows the same rules as accept_any with the difference that the resulting parent futures are unwrapped (have get() called on them). For example:

auto task = tw::make_task(tw::consume_any, [](int x) { return x; }, parent1, parent2);

wait: This task's functor does not take any parameters but the task must have at least one parent. It simply waits for completion of all parents while unwrapping futures before calling the child's functor. For example:

auto task = tw::make_task(tw::wait, []{ return 42; }, parent1, parent2);

wait_any: This task works similar to the wait task but calls its functor as soon as the first parent completes. It abandons and cancels all remaining parent tasks. For example:

auto task = tw::make_task(tw::wait_any, []{ return 42; }, parent1, parent2);

Generally, tasks are created using make_task which allows for any number of parents. However, it is a common use case for a child to only have one parent. For this, next() can be directly called on the parent object to create a continuation:

auto child = tw::make_task(tw::root, []{ return 42; })->next(tw::consume, functor);

child is now a single-parent task whose functor consumes an integer.

Scheduling tasks

Once a task is created it can be scheduled just by itself:

auto task = tw::make_task(tw::root, functor);
task->schedule();

which, if nothing else is specified, will run the task on the current thread. However, using the built-in parallel executor the task can be pushed into a thread pool and executed asynchronously:

tw::parallel executor{4};  // Thread pool with 4 threads
auto task = tw::make_task(tw::root, functor);
task->schedule(executor);

Regardless of how you schedule, the task result can be retrieved through:

std::cout << task->get() << std::endl;

When chaining multiple tasks together a directed acyclic graph is built in which every task can be scheduled individually. Though, in many scenarios it is useful to compute all tasks in the right order with a single call:

auto parent1 = tw::make_task(tw::root, foo);  // foo is a functor
auto parent2 = tw::make_task(tw::root, bar);  // bar is a functor
auto task = tw::make_task(tw::consume, functor, parent1, parent2);
task->schedule_all();  // Schedules all parents and itself

which can also be scheduled using an executor, for instance:

tw::parallel executor{4};
task->schedule_all(executor);

which will run those tasks in parallel that do not depend on each other.

Executors

We have seen that we can pass executors to schedule() and schedule_all(). Additionally, they can be assigned to a task directly:

auto exec1 = std::make_shared<tw::parallel>(2);
task->set_executor(exec1);
tw::sequential exec2;
task->schedule(exec2);  // exec1 will be used to schedule the task

The task-specific executor will always be preferred over other executors when scheduling tasks.

transwarp defines an executor interface which can be implemented to perform custom behavior when scheduling tasks. The interface looks like this:

class executor {
public:
    virtual ~executor() = default;
    
    // The name of the executor
    virtual std::string name() const = 0;
    
    // Only ever called on the thread of the caller to schedule()
    virtual void execute(const std::function<void()>& functor, tw::itask& task) = 0;
};

where functor denotes the function to be run and task the task the functor belongs to.

Range functions

There are convenience functions that can be applied to an iterator range:

  • tw::for_each
  • tw::transform

These are very similar to their standard library counterparts except that they return a task for deferred, possibly asynchronous execution. Here's an example:

std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7};
tw::parallel exec{4};
auto task = tw::for_each(exec, vec.begin(), vec.end(), [](int& x){ x *= 2; });
task->wait();  // all values in vec will have doubled

Canceling tasks

A task can be canceled by calling task->cancel(true) which will, by default, only affect tasks that are not currently running yet. However, if you create a functor that inherits from transwarp::functor you can terminate tasks while they're running. transwarp::functor looks like this:

class functor {
public:
    virtual ~functor() = default;

protected:
    // The associated task (only to be called after the task was constructed)
    const tw::itask& transwarp_task() const noexcept;

    // The associated task (only to be called after the task was constructed)
    tw::itask& transwarp_task() noexcept;

    // If the associated task is canceled then this will throw transwarp::task_canceled
    // which will stop the task while it's running (only to be called after the task was constructed)
    void transwarp_cancel_point() const;

private:
    ...
};

By placing calls to transwarp_cancel_point() in strategic places of your functor you can denote well defined points where the functor will exit when the associated task is canceled. A task can also be canceled by throwing transwarp::task_canceled directly.

As mentioned above, tasks can be explicitly canceled on client request. In addition, all tasks considered abandoned by accept_any, consume_any, or wait_any operations are also canceled in order to terminate them as soon as their computations become superfluous.

Event system

Transwarp provides an event system that allows you to subscribe to all or specific events of a task, such as, before started or after finished events. The task events are enumerated in the event_type enum:

enum class event_type {
    before_scheduled, // Just before a task is scheduled
    after_future_changed, // Just after the task's future was changed
    before_started, // Just before a task starts running
    before_invoked, // Just before a task's functor is invoked
    after_finished, // Just after a task has finished running
    after_canceled, // Just after a task was canceled
    after_satisfied, ///< Just after a task has satisfied all its children with results
    after_custom_data_set, // Just after custom data was assigned
}

Listeners are created by sub-classing from the listener interface:

class listener {
public:
    virtual ~listener() = default;

    // This may be called from arbitrary threads depending on the event type
    virtual void handle_event(tw::event_type event, tw::itask& task) = 0;
};

A listener can then be passed to the add_listener functions of a task to add a new listener or to the remove_listener functions to remove an existing listener.

Task pool

A task pool is useful when one wants to run the same graph in parallel. For this purpose, transwarp provides a task_pool which manages a pool of tasks from which one can request an idle task for parallel graph execution. For example:

tw::parallel exec{4};

auto my_task = make_graph();
tw::task_pool<double> pool{my_task};

for (;;) {
    auto task = pool.next_task(); // task may be null if the pool size is exhausted
    if (task) {
        task->schedule_all(exec);
    }
}

Timing tasks

In order to identify bottlenecks it's often useful to know how much time is spent in which task. transwarp provides a timer listener that will automatically time the tasks it listens to:

auto task = make_graph();
task->add_listener_all(std::make_shared<tw::timer>()); // assigns the timer listener to all tasks
task->schedule_all();
std::ofstream{"graph.dot"} << tw::to_string(task->edges()); // the dot file now contains timing info

Optimizing efficiency

Compile time switches

By default, transwarp provides its full functionality to its client. However, in many cases not all of that is actually required and so transwarp provides a few compile time switches to reduce the task size. These switches are:

TRANSWARP_DISABLE_TASK_CUSTOM_DATA
TRANSWARP_DISABLE_TASK_NAME
TRANSWARP_DISABLE_TASK_PRIORITY
TRANSWARP_DISABLE_TASK_REFCOUNT
TRANSWARP_DISABLE_TASK_TIME

To get the minimal task size with a single switch one can define

TRANSWARP_MINIMUM_TASK_SIZE

at build time.

Releasing unused memory

By default, every task in a graph will keep its result until rescheduling or a manual task reset. The releaser listener allows you to automatically release a task result after that task's children have consumed the result. For example:

auto task = make_graph();
task->add_listener_all(std::make_shared<tw::releaser>()); // assigns the releaser listener to all tasks
task->schedule_all();
// All intermediate task results are now released (i.e. futures are invalid)
auto result = task->get(); // The final task's result remains valid

The releaser also accepts an executor that gives control over where a task's result is released.

Feedback

Get in touch if you have any questions or suggestions to make this a better library! You can post on gitter, submit a pull request, create a Github issue, or simply email one of the contributors.

If you're serious about contributing code to transwarp (which would be awesome!) then please submit a pull request and keep in mind that:

  • unit tests should be added for all new code by extending the existing unit test suite
  • C++ code uses spaces throughout

Contributors

Issues
  • Continue child task execution after parent exception for wait and wait_any types

    Continue child task execution after parent exception for wait and wait_any types

    As wait and wait_any don't take parents' results, execution should continue. This allows for a parent.get() in the child task to also handle error/exception cases.

    opened by tribal-tec 16
  • Control lifetime of task results

    Control lifetime of task results

    The way I understand the current state of affairs is that all tasks (and their results) will live until the end of evaluating the whole task graph. For applications with sizable memory footprint, it would be nice to have a way to control the lifetime of results and mark intermediary objects that do not actually matter eventually as such, so that they can be cleaned up when they are not required by any further tasks.

    Is there a mechanism to achieve this behavior?

    opened by ebatz 11
  • Tasks are not executed

    Tasks are not executed

    My code does not seem to execute at all.

            auto t = transwarp::make_task(transwarp::root,
                [this, id, url] {
                    qCWarning(npackdImportant) << "downloadFileRunnable";
                    return this->downloadFileRunnable(id, url);
                });
            t->add_listener(transwarp::event_type::after_finished,
                    downloadFileListener);
    
            t->schedule(threadPool);
    
    
    opened by tim-lebedkov 5
  • if graph final have many leaf tasks, can transwatp get the final result?

    if graph final have many leaf tasks, can transwatp get the final result?

    because the program finally use the last task excute

    std::shared_ptr<tw::task> build_graph() { auto task0 = tw::make_task(tw::root, func0); auto task1 = tw::make_task(tw::root, func1); auto task2 = tw::make_task(tw::consume, func2, task0, task1); auto task3 = tw::make_task(tw::root, func3); auto task4 = tw::make_task(tw::consume, func4, task2, task3);
    return task4; }

    void calculate_via_transwarp(tw::task& task) { tw::parallel executor{4}; task.schedule_all(executor); long result = task.get();
    std::cout << "transwarp result is : " << result << std::endl; }

    opened by rrjia 4
  • `listener` and `executor` use `itask`

    `listener` and `executor` use `itask`

    Currently, the listener and executor interfaces refer to a shared_ptr of node. This should be changed to a shared_ptr of itask for greater flexibility.

    opened by bloomen 4
  • Trace timings

    Trace timings

    Thank you for the wonderful library.

    What do you think about adding some type of tracing/metrics interface? It would be neat for a given execution of the graph to augment the dot-style graph with timing info.

    help wanted 
    opened by ryanolson 4
  • Comparison with cpp-taskflow

    Comparison with cpp-taskflow

    Thank you for your very hard work!

    I keep an eye on in this library and cpp-taskflow. Both libraries are very alive and they seems powerful. I think a comparison between the two would be interesting.

    I must admit that I have not done anything serious yet but I feel very fortunate and grateful for these projects. Variety in alternatives, aproaches, options makes us all stronger, Thanks again!

    DJuego

    help wanted 
    opened by DJuego 4
  • How to handle sub task

    How to handle sub task

    Hello,

    In case that you create tasks inside a task:

    auto main_task = tw::make_task(tw::wait, [exec]{
    std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7};
    auto sub_task = tw::for_each(exec, vec.begin(), vec.end(), [](int& x){ x *= 2; });
    sub_task-wait();
    });
    

    How can we handle this issue? If we imagine that I have a single worker in my pool, my main task will be paused until my sub task is finished but because the worker is already used by the main_task, my sub_task is blocked because there is no free worker.

    Regards

    opened by Alex-PLACET 3
  • Transwarp doesn't seem to be fully move aware

    Transwarp doesn't seem to be fully move aware

    This could be more of a question than a bug, but is it intended to be able to package move only objects inside tasks? I've been looking at the code, and there seems to be some intent that transwarp is move aware, but it doesn't seem to be complete.

    Minimum repo on VS2019 (v142) or Clang on godbolt: transwarp::make_task(transwarp::root, []() { return std::make_unique<int>(); });

    What happens is that because transwarp::task has both overloads for set_value: virtual void set_value(const typename transwarp::decay<result_type>::type& value) = 0; virtual void set_value(typename transwarp::decay<result_type>::type&& value) = 0;

    ...both functions get instantiated in task_impl_proxy. With movable only ResultTypes, the first function will still get instantiated, but it will be invalid C++ as the function's implementation will eventually attempt to make a copy.

    Is it intended that this does not work?

    Cheers

    opened by aheysse 3
  • Merge `node` into `itask`

    Merge `node` into `itask`

    The goal is to remove confusion between task and node. There's no significant argument anymore to keep node around so the goal is to merge its fields (minus executor) into itask and add corresponding accessor methods. itask may have to be renamed pending discussions.

    opened by bloomen 3
  • next() method documented but does not exist

    next() method documented but does not exist

    Hello,

    I saw this part in the documentation:

    Generally, tasks are created using make_task which allows for any number of parents. However, it is a common use case for a child to only have one parent. For this, next() can be directly called on the parent object to create a continuation: auto child = tw::make_task(tw::root, []{ return 42; })->next(tw::consume, functor);

    I don't see anything related to this method in the code. Is it something planned or removed ?

    Great work btw

    opened by Alex-PLACET 2
  • Measure unit test coverage

    Measure unit test coverage

    The goal is to run the tests and then do make coverage to generate a coverage report in HTML format. For this to work, we'd have to add a section to our cmake config.

    help wanted 
    opened by bloomen 0
Releases(2.2.3)
  • 2.2.3(Jun 24, 2022)

  • 2.2.2(Apr 21, 2021)

  • 2.2.1(Nov 23, 2019)

  • 2.2.0(Nov 10, 2019)

    • added compile time switches to control the task size
    • added the releaser listener to allow releasing intermediate results
    • added a callback to the parallel executor to do something on thread start (like thread naming)
    • improved documentation and tests
    Source code(tar.gz)
    Source code(zip)
  • 2.1.0(Apr 20, 2019)

    • added new events: after_future_changed, after_custom_data_set
    • made members of functor and edge mutable and added corresponding accessors
    • removed const from the task parameter of the listener and executor interfaces
    • refactored common stuff between task_impl and value_task into a new base class
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Apr 19, 2019)

    • breaking changes!
    • switched to C++17 (retained transwarp1.X branch for C++11 support)
    • removed transwarp::node and merged its members into the task classes
    • renamed some methods and changed method signatures
    • now using std::optional and std::any
    • added ability to clone tasks and used that in the task_pool
    Source code(tar.gz)
    Source code(zip)
  • 1.9.0(Dec 1, 2018)

    • added for_each and transform free functions
    • added get_task_count and get_parent_count methods to the task class
    • performance optimizations regarding scheduling tasks
    • split up the test suite into multiple files
    • improved documentation
    Source code(tar.gz)
    Source code(zip)
  • 1.8.1(Nov 22, 2018)

  • 1.8.0(Sep 17, 2018)

    • added a timer that can be added as a listener (node will carry the timing info)
    • added methods to add/remove listeners to/from all tasks in the graph
    • improved documentation
    Source code(tar.gz)
    Source code(zip)
  • 1.7.0(Jul 18, 2018)

  • 1.6.1(Jul 8, 2018)

  • 1.6.0(Jun 9, 2018)

    • added support for parents provided as a std::vector of tasks
    • ensured that abandoned tasks are canceled
    • added new events: before_invoked and after_canceled
    • fixed bug where tasks were still canceled after reset
    Source code(tar.gz)
    Source code(zip)
  • 1.5.0(Jun 2, 2018)

    • added a 'then()' method to chain tasks when a child only has one parent
    • added more useful functions to add/remove listeners
    • corrected the naming of the events
    • ensured that a value task can store volatile types
    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(May 26, 2018)

    • added a schedule type enum and made 'breadth' the default
    • added a listener interface to handle task events
    • added a new example that demonstrates scheduling a wide graph
    • improved the performance of scheduling a graph
    • fixed up documentation, added doxygen doc
    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Apr 20, 2018)

    • added new task types: accept and accept_any
    • added new functions: set_value and set_exception
    • added a value_task class that doesn't require scheduling
    • improved error reporting
    • general code clean up; addressed compiler warnings
    • simplify the node class by addressing the constructors
    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(Jan 21, 2018)

  • 1.2.0(Jan 20, 2018)

    • added means of canceling a task while its running through transwarp::functor
    • added method 'was_scheduled()' to the task class to check if the task was scheduled
    • added method 'is_ready()' to check if a result is available
    • added new methods to the task class that forward to all parent tasks
    • added safety checks to methods that shouldn't be called when a task is running
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Jan 12, 2018)

    • added 'wait()' method to wait for the task to finish
    • added 'get()' method to retrieve the task result
    • added methods to assign/query priority and custom data of a task
    • improved documentation
    Source code(tar.gz)
    Source code(zip)
  • 1.0.1(Oct 31, 2017)

  • 1.0.0(Sep 30, 2017)

    • made the library safer and more user-friendly
    • merge final_task and task together to a single class
    • add task types to support consuming and waiting
    • added ability to assign custom executors per task
    • changed schedule functions to accept an executor
    • improved documentation and examples
    • switch to using cmake and catch
    • add continuous integration on Linux, Mac, and Windows
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Jul 20, 2017)

    • added ability to use custom executors
    • added ability to specify a priority for tasks and their execution
    • added noexcept keyword where possible
    • removed ability to pause execution
    • better documentation
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(May 15, 2017)

Owner
Christian Blume
Christian Blume
Task System presented in "Better Code: Concurrency - Sean Parent"

task_system task_system provides a task scheduler for modern C++. The scheduler manages an array of concurrent queues A task, when scheduled, is enque

Pranav 27 Jun 16, 2022
Cpp-concurrency - cpp implementation of golang style concurrency

cpp-concurrency C++ implementation of golang style concurrency Usage Use existing single header concurrency.hpp or run script to merge multiple header

YoungJoong Kim 15 Apr 30, 2021
The C++ Standard Library for Parallelism and Concurrency

Documentation: latest, development (master) HPX HPX is a C++ Standard Library for Concurrency and Parallelism. It implements all of the corresponding

The STE||AR Group 1.9k Jun 23, 2022
HPX is a C++ Standard Library for Concurrency and Parallelism

HPX is a C++ Standard Library for Concurrency and Parallelism. It implements all of the corresponding facilities as defined by the C++ Standard. Additionally, in HPX we implement functionalities proposed as part of the ongoing C++ standardization process. We also extend the C++ Standard APIs to the distributed case.

The STE||AR Group 1.9k Jun 28, 2022
Yet Another Concurrency Library

YACLib YACLib (Yet Another Concurrency Library) is a C++ library for concurrent tasks execution. Documentation Install guide About dependencies Target

null 119 Jun 9, 2022
OOX: Out-of-Order Executor library. Yet another approach to efficient and scalable tasking API and task scheduling.

OOX Out-of-Order Executor library. Yet another approach to efficient and scalable tasking API and task scheduling. Try it Requirements: Install cmake,

Intel Corporation 17 Mar 10, 2022
Cpp-taskflow - Modern C++ Parallel Task Programming Library

Cpp-Taskflow A fast C++ header-only library to help you quickly write parallel programs with complex task dependencies Why Cpp-Taskflow? Cpp-Taskflow

null 4 Mar 30, 2021
The RaftLib C++ library, streaming/dataflow concurrency via C++ iostream-like operators

RaftLib is a C++ Library for enabling stream/data-flow parallel computation. Using simple right shift operators (just like the C++ streams that you wo

RaftLib 799 Jun 23, 2022
C++14 coroutine-based task library for games

SquidTasks Squid::Tasks is a header-only C++14 coroutine-based task library for games. Full project and source code available at https://github.com/we

Tim Ambrogi Saxon 39 Jun 23, 2022
Async++ concurrency framework for C++11

Async++ Async++ is a lightweight concurrency framework for C++11. The concept was inspired by the Microsoft PPL library and the N3428 C++ standard pro

Amanieu d'Antras 1.1k Jun 20, 2022
Concurrency Kit 2.1k Jun 18, 2022
A General-purpose Parallel and Heterogeneous Task Programming System

Taskflow Taskflow helps you quickly write parallel and heterogeneous tasks programs in modern C++ Why Taskflow? Taskflow is faster, more expressive, a

Taskflow 6.9k Jun 23, 2022
A task scheduling framework designed for the needs of game developers.

Intel Games Task Scheduler (GTS) To the documentation. Introduction GTS is a C++ task scheduling framework for multi-processor platforms. It is design

null 412 Jun 11, 2022
The libdispatch Project, (a.k.a. Grand Central Dispatch), for concurrency on multicore hardware

Grand Central Dispatch Grand Central Dispatch (GCD or libdispatch) provides comprehensive support for concurrent code execution on multicore hardware.

Apple 2.2k Jun 19, 2022
Go-style concurrency in C

LIBMILL Libmill is a library that introduces Go-style concurrency to C. Documentation For the documentation check the project website: http://libmill.

Martin Sustrik 2.6k Jun 23, 2022
A hybrid thread / fiber task scheduler written in C++ 11

Marl Marl is a hybrid thread / fiber task scheduler written in C++ 11. About Marl is a C++ 11 library that provides a fluent interface for running tas

Google 1.4k Jun 24, 2022
Modern concurrency for C++. Tasks, executors, timers and C++20 coroutines to rule them all

concurrencpp, the C++ concurrency library concurrencpp is a tasking library for C++ allowing developers to write highly concurrent applications easily

David Haim 950 Jun 24, 2022
Complementary Concurrency Programs for course "Linux Kernel Internals"

Complementary Programs for course "Linux Kernel Internals" Project Listing tpool: A lightweight thread pool. tinync: A tiny nc implementation using co

null 221 Jun 10, 2022
A General-purpose Parallel and Heterogeneous Task Programming System

Taskflow Taskflow helps you quickly write parallel and heterogeneous task programs in modern C++ Why Taskflow? Taskflow is faster, more expressive, an

Taskflow 6.9k Jun 22, 2022