All Projects → YACLib → YACLib

YACLib / YACLib

Licence: MIT license
Yet Another Concurrency Library

Programming Languages

C++
36643 projects - #6 most used programming language
CMake
9771 projects
python
139335 projects - #7 most used programming language

Projects that are alternatives of or similar to YACLib

Hamsters.js
100% Vanilla Javascript Multithreading & Parallel Execution Library
Stars: ✭ 517 (+167.88%)
Mutual labels:  thread, concurrency, parallel, parallelism, thread-pool, concurrent
ProtoPromise
Robust and efficient library for management of asynchronous operations in C#/.Net.
Stars: ✭ 20 (-89.64%)
Mutual labels:  task, promise, concurrency, parallel, coroutine
haxe-concurrent
A haxelib for basic platform-agnostic concurrency support
Stars: ✭ 69 (-64.25%)
Mutual labels:  executor, scheduler, thread, concurrency, thread-pool
Unityfx.async
Asynchronous operations (promises) for Unity3d.
Stars: ✭ 143 (-25.91%)
Mutual labels:  task, promise, future, coroutine
java-multithread
Códigos feitos para o curso de Multithreading com Java, no canal RinaldoDev do YouTube.
Stars: ✭ 24 (-87.56%)
Mutual labels:  concurrency, parallel, parallelism, concurrent
Util
A collection of useful utility functions
Stars: ✭ 201 (+4.15%)
Mutual labels:  concurrency, parallel, parallelism, concurrent
Java Concurrency Examples
Java Concurrency/Multithreading Tutorial with Examples for Dummies
Stars: ✭ 173 (-10.36%)
Mutual labels:  thread, concurrency, thread-pool, future
parallelizer
Simplifies the parallelization of function calls.
Stars: ✭ 62 (-67.88%)
Mutual labels:  job, parallel, parallelism
Bree
🚥 The best job scheduler for Node.js and JavaScript with cron, dates, ms, later, and human-friendly support. Works in Node v10+ and browsers, uses workers to spawn sandboxed processes, and supports async/await, retries, throttling, concurrency, and graceful shutdown. Simple, fast, and lightweight. Made for @ForwardEmail and @ladjs.
Stars: ✭ 933 (+383.42%)
Mutual labels:  job, scheduler, concurrency
Unitask
Provides an efficient allocation free async/await integration for Unity.
Stars: ✭ 2,547 (+1219.69%)
Mutual labels:  task, thread, coroutine
Flowa
🔥Service level control flow for Node.js
Stars: ✭ 66 (-65.8%)
Mutual labels:  task, promise, parallel
Transmittable Thread Local
📌 TransmittableThreadLocal (TTL), the missing Java™ std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.
Stars: ✭ 4,678 (+2323.83%)
Mutual labels:  executor, thread, thread-pool
Tascalate Concurrent
Implementation of blocking (IO-Bound) cancellable java.util.concurrent.CompletionStage and related extensions to java.util.concurrent.ExecutorService-s
Stars: ✭ 144 (-25.39%)
Mutual labels:  promise, concurrency, concurrent
ComposableAsync
Create, compose and inject asynchronous behaviors in .Net Framework and .Net Core.
Stars: ✭ 28 (-85.49%)
Mutual labels:  task, thread, concurrency
Kommander Ios
A lightweight, pure-Swift library for manage the task execution in different threads. Through the definition a simple but powerful concept, Kommand.
Stars: ✭ 167 (-13.47%)
Mutual labels:  task, thread, concurrency
practice
Java并发编程与高并发解决方案:http://coding.imooc.com/class/195.html Java开发企业级权限管理系统:http://coding.imooc.com/class/149.html
Stars: ✭ 39 (-79.79%)
Mutual labels:  executor, concurrency, concurrent
Xxl Job
A distributed task scheduling framework.(分布式任务调度平台XXL-JOB)
Stars: ✭ 20,197 (+10364.77%)
Mutual labels:  task, job, scheduler
concurrency-kit
🚄 Concurrency abstractions framework for Apple Platforms [Task, Atomic, Lock, Operation, etc.].
Stars: ✭ 17 (-91.19%)
Mutual labels:  task, concurrency, concurrent
Fun Task
Abstraction for managing asynchronous code in JS
Stars: ✭ 363 (+88.08%)
Mutual labels:  task, promise, future
Taskmanager
A simple、 light(only two file)、fast 、powerful 、easy to use 、easy to extend 、 Android Library To Manager your AsyncTask/Thread/CallBack Jobqueue ! 一个超级简单,易用,轻量级,快速的异步任务管理器,类似于AsyncTask,但是比AsyncTask更好用,更易控制,从此不再写Thread ! ^_^
Stars: ✭ 25 (-87.05%)
Mutual labels:  task, job, thread

YACLib

Yet Another Concurrency Library

GitHub license FOSSA status

Linux macOS Windows Sanitizers

Test coverage: coveralls Test coverage: codecov Codacy Badge

Discord

Table of Contents

About YACLib

YACLib is a lightweight C++ library for concurrent and parallel task execution, that is striving to satisfy the following properties:

  • Zero cost abstractions
  • Easy to use
  • Easy to build
  • Good test coverage

For more details check our design document and documentation.

Getting started

For quick start just paste this code in your CMakeLists.txt file.

include(FetchContent)
FetchContent_Declare(yaclib
  GIT_REPOSITORY https://github.com/YACLib/YACLib.git
  GIT_TAG main
)
FetchContent_MakeAvailable(yaclib)
link_libraries(yaclib)

For more details check install guide.

For more details about 'yaclib_std' or fault injection, check doc.

Examples

Here are short examples of using some features from YACLib, for details check documentation.

Asynchronous pipeline

yaclib::FairThreadPool cpu_tp{/*threads=*/4};
yaclib::FairThreadPool io_tp{/*threads=*/1};

yaclib::Run(cpu_tp, [] {  // on cpu_tp
  return 42;
}).ThenInline([](int r) {  // called directly after 'return 42', without Submit to cpu_tp
  return r + 1;
}).Then([](int r) {  // on cpu_tp
  return std::to_string(r);
}).Detach(io_tp, [](std::string&& r) {  // on io_tp
  std::cout << "Pipeline result: <"  << r << ">" << std::endl; // 43
});

We guarantee that no more than one allocation will be made for each step of the pipeline.

We have Then/Detach x IExecutor/previous step IExecutor/Inline.

Also Future/Promise don't contain shared atomic counters!

C++20 coroutine

yaclib::Future<int> task42() {
  co_return 42;
}

yaclib::Future<int> task43() {
  auto value = co_await task42();
  co_return value + 1;
}

You can zero cost-combine Future coroutine code with Future callbacks code. That allows using YAClib for a smooth transfer from C++17 to C++20 with coroutines.

Also Future with coroutine doesn't make additional allocation for Future, only coroutine frame allocation that is caused by compiler, and can be optimized.

And finally co_await doesn't require allocation, so you can combine some async operation without allocation.

Lazy pipeline

auto task = yaclib::Schedule(tp1, [] {
  return 1; 
}).Then([] (int x) {
  return x * 2;
});

task.Run(); // Run task on tp1

Same as asynchronous pipeline, but starting only after Run/ToFuture/Get. Task can be used as coroutine return type too.

Also running a Task that returns a Future doesn't make allocation. And it doesn't need synchronization, so it is even faster than asynchronous pipeline.

Thread pool

yaclib::FairThreadPool tp{/*threads=*/4};
Submit(tp, [] {
  // some computations...
});
Submit(tp, [] {
  // some computations...
});

tp.Stop();
tp.Wait();

Strand, Serial executor

yaclib::FairThreadPool tp{/*threads=*/4};
// decorated thread pool by serializing tasks:
auto strand = yaclib::MakeStrand(&tp);

std::size_t counter = 0;
for (std::size_t i = 0; i < 100; ++i) {
  Submit(tp, [&] {
    Submit(*strand, [&] {
      // serialized by Strand, no data race!
      ++counter; 
    });
  });
}

This is much more efficient than a mutex because

  1. don't block the threadpool thread.
  2. we execute critical sections in batches (the idea is known as flat-combining).

And also the implementation of strand is lock-free and efficient, without additional allocations.

Mutex

yaclib::FairThreadPool tp{4};
yaclib::Mutex<> m;

std::size_t counter = 0;

auto compute = [&] (std::size_t index) -> yaclib::Future<> {
  co_await On(tp);
  // ... some computation with index ...
  auto guard = co_await m.Guard();
  // ... co_await On(other thread pool) ...
  counter += index;
};

for (std::size_t i = 0; i < 100; ++i) {
  compute(i);
}

First, this is the only correct mutex implementation for C++20 coroutines as far as I know (cppcoro, libunifex, folly::coro implement Unlock incorrectly, it serializes the code after Unlock)

Second, Mutex inherits all the Strand benefits.

Rescheduling

yaclib::Future<> bar(yaclib::IExecutor& cpu, yaclib::IExecutor& io) {
  co_await On(cpu);
  // ... some heavy computation ...
  co_await On(io);
  // ... some io computation ...
}

This is really zero-cost, just suspend the coroutine and submit its resume to another executor, without synchronization inside the coroutine and allocations anywhere.

WhenAll

yaclib::FairThreadPool tp{/*threads=*/4};
std::vector<yaclib::Future<int>> fs;

// Run parallel computations
for (std::size_t i = 0; i < 5; ++i) {
  fs.push_back(yaclib::Run(tp, [i]() -> int {
    return random() * i;
  }));
}

// Will be ready when all futures are ready
yaclib::Future<std::vector<int>> all = WhenAll(fs.begin(), fs.size());
std::vector<int> unique_ints = std::move(all).Then([](std::vector<int> ints) {
  ints.erase(std::unique(ints.begin(), ints.end()), ints.end());
  return ints;
}).Get().Ok();

Doesn't make more than 3 allocations regardless of input size.

WhenAny

yaclib::FairThreadPool tp{/*threads=*/4};
std::vector<yaclib::Future<int>> fs;

// Run parallel computations
for (std::size_t i = 0; i < 5; ++i) {
  fs.push_back(yaclib::Run(tp, [i] {
    // connect with one of the database shards
    return i;
  }));
}

// Will be ready when any future is ready
WhenAny(fs.begin(), fs.size()).Detach([](int i) {
  // some work with database
});

Doesn't make more than 2 allocations regardless of input size.

Future unwrapping

yaclib::FairThreadPool tp_output{/*threads=*/1};
yaclib::FairThreadPool tp_compute{/*threads=CPU cores*/};

auto future = yaclib::Run(tp_output, [] {
  std::cout << "Outer task" << std::endl;
  return yaclib::Run(tp_compute, [] { return 42; });
}).Then(/*tp_compute*/ [](int result) {
  result *= 13;
  return yaclib::Run(tp_output, [result] { 
    std::cout << "Result = " << result << std::endl; 
  });
});

Sometimes it's necessary to return from one async function the result of the other. It would be possible with the wait on this result. But this would cause blocking of the thread while waiting for the task to complete.

This problem can be solved using future unwrapping: when an async function returns a Future object, instead of setting its result to the Future object, the inner Future will "replace" the outer Future. This means that the outer Future will complete when the inner Future finishes and will acquire the result of the inner Future.

It also doesn't require additional allocations.

Timed wait

yaclib::FairThreadPool tp{/*threads=*/4};

yaclib::Future<int> f1 = yaclib::Run(tp, [] { return 42; });
yaclib::Future<double> f2 = yaclib::Run(tp, [] { return 15.0; });

WaitFor(10ms, f1, f2);  // or Wait / WaitUntil

if (f1.Ready()) {
  Process(std::as_const(f1).Get());
  yaclib::Result<int> res1 = std::as_const(f1).Get();
  assert(f1.Valid());  // f1 valid here
}

if (f2.Ready()) {
  Process(std::move(f2).Get());
  assert(!f2.Valid());  // f2 invalid here
}

We support Wait/WaitFor/WaitUntil. Also all of them don't make allocation, and we have optimized the path for single Future (used in Future::Get()).

WaitGroup

yaclib::WaitGroup wg{1};

yaclib::FairThreadPool tp;

wg.Add(2/*default=1*/);
Submit(tp, [] {
   wg.Done();
});
Submit(tp, [] {
   wg.Done();
});

yaclib::Future<int> f1 = yaclib::Run(tp, [] {...});
wg.Attach(f1);  // auto Done then Future became Ready

yaclib::Future<> f2 = yaclib::Run(tp, [] {...});
wg.Consume(std::move(f2));  // auto Done then Future became Ready

auto coro = [&] () -> yaclib::Future<> {
  co_await On(tp);
  co_await wg; // alias for co_await wg.Await(CurrentThreadPool());
  std::cout << f1.Touch().Ok(); // Valid access to Result of Ready Future
};

auto coro_f = coro();

wg.Done(/*default=1*/);
wg.Wait();

Effective like simple atomic counter in intrusive pointer, also doesn't require any allocation.

Exception recovering

yaclib::FairThreadPool tp{/*threads=*/4};

auto f = yaclib::Run(tp, [] {
  if (random() % 2) {
    throw std::runtime_error{"1"};
  }
  return 42;
}).Then([](int y) {
  if (random() % 2) {
    throw std::runtime_error{"2"};
  }
  return y + 15;
}).Then([](int z) {  // Will not run if we have any error
  return z * 2;
}).Then([](std::exception_ptr e) {  // Recover from error codes
  try {
    std::rethrow_exception(e);
  } catch (const std::runtime_error& e) {
    std::cout << e.what() << std::endl;
  }
  return 10;  // Some default value
});
int x = std::move(f).Get().Value();

Error recovering

yaclib::FairThreadPool tp{/*threads=*/4};

auto f = yaclib::Run<std::error_code>(tp, [] {
  if (random() % 2) {
    return std::make_error_code(1);
  }
  return 42;
}).Then([](int y) {
  if (random() % 2) {
    return std::make_error_code(2);
  }
  return y + 15;
}).Then([](int z) {  // Will not run if we have any error
  return z * 2;
}).Then([](std::error_code ec) {  // Recover from error codes
  std::cout << ec.value() << std::endl;
  return 10;  // Some default value
});
int x = std::move(f).Get().Value();

Use Result for smart recovering

yaclib::FairThreadPool tp{/*threads=*/4};

auto f = yaclib::Run(tp, [] {
  if (random() % 2) {
    return std::make_error_code(1);
  }
  return 42;
}).Then([](int y) {
  if (random() % 2) {
    throw std::runtime_error{"2"};
  }
  return y + 15;
}).Then([](yaclib::Result<int>&& z) {
  if (!z) {
    return 10;  // Some default value
  }
  return std::move(z).Value();
});
int x = std::move(f).Get().Value();

Requirements

YACLib is a static library, that uses CMake as a build system and requires a compiler with C++17 or newer.

If the library doesn't compile on some compiler satisfying this condition, please create an issue. Pull requests with fixes are welcome!

We can also try to support older standards. If you are interested in it, check this discussion.

We test following configurations:

- CI tested

👌 - manually tested

Compiler\OS Linux Windows macOS Android
GCC 7+ 👌 MinGW 7+ 👌
Clang 8+ ClangCL 8+ 👌
AppleClang 12+
MSVC 14.20+

MinGW works in CI early, check this.

Releases

YACLib follows the Abseil Live at Head philosophy (update to the latest commit from the main branch as often as possible).

So we recommend using the latest commit in the main branch in your projects.

This is safe because we suggest compiling YACLib from source, and each commit in main goes through dozens of test runs in various configurations. Our test coverage is 100%, to simplify, we run tests on the cartesian product of possible configurations:

os x compiler x stdlib x sanitizer x fault injection backend

However, we realize this philosophy doesn't work for every project, so we also provide Releases.

We don't believe in SemVer (check this), but we use a year.month.day[.patch] versioning approach. I'll release a new version if you ask, or I'll decide we have important or enough changes.

Contributing

We are always open for issues and pull requests. Check our good first issues.

For more details you can check the following links:

Thanks

Contacts

You can contact us by my email: [email protected]

Or join our Discord Server

License

YACLib is made available under MIT License. See LICENSE file for details.

We would be glad if you let us know that you're using our library.

FOSSA Status

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].