Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hongtae/corodispatchqueue
Simple Concurrency Library with Coroutine Dispatch Queue
https://github.com/hongtae/corodispatchqueue
async async-await asynchronous asynchronous-programming concurrency concurrency-library coroutines cpp-coroutines cpp-programming cpp20 dispatchqueue multithreading parallelism single-header-library threadpool
Last synced: 17 days ago
JSON representation
Simple Concurrency Library with Coroutine Dispatch Queue
- Host: GitHub
- URL: https://github.com/hongtae/corodispatchqueue
- Owner: Hongtae
- License: apache-2.0
- Created: 2024-06-15T14:20:00.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2024-08-13T03:09:59.000Z (5 months ago)
- Last Synced: 2025-01-01T08:13:36.083Z (21 days ago)
- Topics: async, async-await, asynchronous, asynchronous-programming, concurrency, concurrency-library, coroutines, cpp-coroutines, cpp-programming, cpp20, dispatchqueue, multithreading, parallelism, single-header-library, threadpool
- Language: C++
- Homepage:
- Size: 36.1 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Cpp Coroutine Dispatch Queue
C++ concurrency library with C++20 coroutines.This project is a single header library.
Just include ["DispatchQueue.h"](DispatchQueue.h) in your project.See the example file for more details: [Sample code (test.cpp)](test.cpp)
> [!NOTE]
> The sample project is for Visual Studio 2022.**Requires a C++20 or later compiler.**
## Types
- Task
- Common coroutine function type
```
Task getValue() {
co_return 1;
}int n = co_await getValue();
```
- Generator
- Function type that yields multiple values without returning
```
Generator gen(int n) {
for (int i = 0; i < n; ++i)
co_yield i;
}for (auto g = gen(10); co_await g; ) {
std::cout << "value: " << g.value();
}
```
- AsyncTask
- Asynchronous function type that runs on a specific dispatch queue.
- When it completes execution, it returns to the initial dispatch queue.
```
AsyncTask getValueThread() {
std::cout << "this_thread: " << std::this_thread::get_id();
co_return 0;
}// Running on a background dispatch-queue and then returning to the original dispatch-queue.
// NOTE: Returning to the same dispatch queue does not mean the same thread.
// If the current dispatch queue owns multiple threads, it may return to a different thread than it was before the call.
int value = getValueThread();
```
- AsyncGenerator
- Asynchronous function type that runs on a specific dispatch queue.
- This function can yield multiple values to the caller without returning.
```
AsyncGenerator gen(int n) {
for (int i = 0; i < n; ++i) {
std::cout << "callee-thread: " << std::this_thread::get_id();
co_yield i;
}
}for (auto g = gen(10); co_await g; ) {
std::cout << "caller-thread: " << std::this_thread::get_id();
std::cout << "value: " << g.value();
}
```
- DispatchQueue
- A queue that manages threads. A queue can have multiple threads.
- See the source code for a detailed implementation.- TaskGroup
- Group object for executing coroutine functions in parallel- AsyncTaskGroup
- Group object for executing coroutine functions in parallel
- Runs on a specific dispatch queue and returns to the original dispatch queue when it completes.## Functions
- detachTask(AsyncTask)
- detachTask(Task)
- Run the coroutine.
- This function can be called in a synchronous context.
- This is the entry function that spawns the asynchronous context.- asyncSleep(double)
- Suspends execution for a given amount of time, but does not actually sleep the thread.- asyncYield()
- Yield execution to another coroutine function.- async(TaskGroup)
- async(AsyncTaskGroup)
- Run a TaskGroup with multiple coroutine functions.## Usage
```
// async function using background thread.
Async<> func1() {
co_await asyncSleep(10.0); // sleep 10s
co_return;
}// async function that returns int.
Async func2() {
int n = co_await doSomething();
co_return n;
}// a coroutine function that returns float.
Task func3() {
co_return 1.0f;
}
``````
// async generator to yield some values
AsyncGenerator asyncGen(int n) {
for (int i = 0; i < n; ++i)
co_yield i;
}auto x = asyncGen(10);
while (co_await x) {
printf("yield: %d", x.value());
}
``````
// coroutine generator to yield INT values
Generator generator(int n) {
for (int i = 0; i < n; ++i)
co_yield i;
}auto x = generator(3);
while (co_await x) {
printf("yield: %d", x.value());
}
``````
Async<> test() {
co_await asyncSleep(10.0);
co_return;
}// run a new task in a background thread
dispatchTask(test());// run multiple new tasks in a background thread
auto group = AsyncTaskGroup{ test(), test(), test() }
co_await async(group);
``````
// run multiple new tasks that return values in a background thread
Async func1(int n) {
co_return n * 2;
}auto group = AsyncTaskGroup({ func1(3), func1(10), func1(20) });
auto x = co_await async(group);
while (co_await x) {
printf("yield: %d", x.value());
}
``````
// switching the running task threadDispatchQueue myQueue(3); // my queue with 3 threads
Async<> work() {
std::cout << std::this_thread::get_id() << std::end;co_await myQueue; // switching thread.
std::cout << std::this_thread::get_id() << std::end;
co_return;
}
```
### Registering main thread
- To use dispatchMain(), the main thread must be enabled.
```
std::atomic_flag stop_running;
int main() {
setDispatchQueueMainThread(); // Set the current thread as the main thread.// activate main loop
auto& dq = DispatchQueue::main();
while (!stop_running.test()) {
if (dq.dispatcher()->dispatch() == 0) {
dq.dispatcher()->wait();
}
}
```## Tips & Tricks
#### Using Win32 Main-thread as MainQueue
- What if you can't control the main message loop?
- Use timer, run the following code once on the main thread.
```
TIMERPROC timerProc = [](HWND, UINT, UINT_PTR, DWORD) {
auto& dq = dispatchMain();
while (dq.dispatcher()->dispatch()) {}
};
setDispatchQueueMainThread();
SetTimer(nullptr, 0, USER_TIMER_MINIMUM, timerProc);
```
- Install a timer in the main loop. With TIMEPROC, it is not affected by the modal-state of the Win32 message loop.
- Using Win32 Timer will result in a resolution of 10ms minimum, but we don't expect this to be a problem for asynchronous function execution in general.