You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-[coro::thread_pool](#thread_pool) for coroutine cooperative multitasking
29
29
-[coro::io_scheduler](#io_scheduler) for driving i/o events
30
-
- Can use coro::thread_pool for latency sensitive or long lived tasks.
30
+
- Can use `coro::thread_pool` for latency sensitive or long lived tasks.
31
31
- Can use inline task processing for thread per core or short lived tasks.
32
-
- Currently uses an epoll driver
32
+
- Currently uses an epoll driver, only supported on linux.
33
33
-[coro::task_container](#task_container) for dynamic task lifetimes
34
34
* Coroutine Networking
35
35
- coro::net::dns::resolver for async dns
36
36
- Uses libc-ares
37
37
-[coro::net::tcp::client](#io_scheduler)
38
38
-[coro::net::tcp::server](#io_scheduler)
39
+
*[Example TCP/HTTP Echo Server](#tcp_echo_server)
39
40
- coro::net::tls::client (OpenSSL)
40
41
- coro::net::tls::server (OpenSSL)
41
42
- coro::net::udp::peer
42
-
*[Example TCP/HTTP Echo Server](#tcp_echo_server)
43
-
43
+
*
44
44
*[Requirements](#requirements)
45
45
*[Build Instructions](#build-instructions)
46
-
*[Testing](#tests)
46
+
*[Contributing](#contributing)
47
47
*[Support](#support)
48
48
49
49
## Usage
50
50
51
-
### A note on co_await
52
-
Its important to note with coroutines that depending on the construct used _any_`co_await` has the potential to switch the thread that is executing the currently running coroutine. In general this shouldn't affect the way any user of the library would write code except for `thread_local`. Usage of `thread_local` should be extremely careful and _never_ used across any `co_await` boundary do to thread switching and work stealing on thread pools.
51
+
### A note on co_await and threads
52
+
Its important to note with coroutines that _any_`co_await` has the potential to switch the underyling thread that is executing the currently executing coroutine if the scheduler used has more than 1 thread. In general this shouldn't affect the way any user of the library would write code except for `thread_local`. Usage of `thread_local` should be extremely careful and _never_ used across any `co_await` boundary do to thread switching and work stealing on libcoro's schedulers. The only way this is safe is by using a `coro::thread_pool` with 1 thread or an inline `io_scheduler` which also only has 1 thread.
53
+
54
+
### sync_wait
55
+
The `sync_wait` construct is meant to be used outside of a coroutine context to block the calling thread until the coroutine has completed. The coroutine can be executed on the calling thread or scheduled on one of libcoro's schedulers.
56
+
57
+
```C++
58
+
${EXAMPLE_CORO_SYNC_WAIT}
59
+
```
60
+
61
+
Expected output:
62
+
```bash
63
+
$ ./examples/coro_sync_wait
64
+
Inline Result = 10
65
+
Offload Result = 20
66
+
```
67
+
68
+
### when_all
69
+
The `when_all` construct can be used within coroutines to await a set of tasks, or it can be used outside coroutinne context in conjunction with `sync_wait` to await multiple tasks. Each task passed into `when_all` will initially be executed serially by the calling thread so it is recommended to offload the tasks onto a scheduler like `coro::thread_pool` or `coro::io_scheduler` so they can execute in parallel.
70
+
71
+
```C++
72
+
${EXAMPLE_CORO_WHEN_ALL}
73
+
```
74
+
75
+
Expected output:
76
+
```bash
77
+
$ ./examples/coro_when_all
78
+
2
79
+
4
80
+
6
81
+
8
82
+
10
83
+
first: 1.21 second: 20
84
+
```
53
85
54
86
### task
55
-
The `coro::task<T>` is the main coroutine building block within `libcoro`. Use task to create your coroutines and `co_await` or `co_yield` tasks within tasks to perform asynchronous operations, lazily evaluation or even spreading work out across a `coro::thread_pool`. Tasks are lightweight and only begin execution upon awaiting them. If their return type is not `void` then the value can be returned by const reference or by moving (r-value reference).
87
+
The `coro::task<T>` is the main coroutine building block within `libcoro`. Use task to create your coroutines and `co_await` or `co_yield` tasks within tasks to perform asynchronous operations, lazily evaluation or even spreading work out across a `coro::thread_pool`. Tasks are lightweight and only begin execution upon awaiting them.
56
88
57
89
58
90
```C++
@@ -474,21 +506,27 @@ target_link_libraries(${PROJECT_NAME} PUBLIC libcoro)
474
506
##### Package managers
475
507
libcoro is available via package managers [Conan](https://conan.io/center/libcoro) and [vcpkg](https://vcpkg.io/).
476
508
509
+
### Contributing
510
+
511
+
Contributing is welcome, if you have ideas or bugs please open an issue. If you want to open a PR they are also welcome, if you are adding a bugfix or a feature please include tests to verify the feature or bugfix is working properly. If it isn't included I will be asking for you to add some!
512
+
477
513
#### Tests
478
-
The tests will automatically be run by github actions on creating a pull request. They can also be ran locally:
514
+
The tests will automatically be run by github actions on creating a pull request. They can also be ran locally after building from the build directory:
479
515
480
516
# Invoke via cmake with all output from the tests displayed to console:
481
517
ctest -VV
482
518
483
519
# Or invoke directly, can pass the name of tests to execute, the framework used is catch2.
484
-
# Tests are tagged with their group, below is howt o run all of the coro::net::tcp::server tests:
485
-
./Debug/test/libcoro_test "[tcp_server]"
520
+
# Tests are tagged with their group, below is how to run all of the coro::net::tcp::server tests:
521
+
./test/libcoro_test "[tcp_server]"
522
+
523
+
If you open a PR for a bugfix or new feature please include tests to verify that the change is working as intended. If your PR doesn't include tests I will ask you to add them and won't merge until they are added and working properly. Tests are found in the `/test` directory and are organized by object type.
486
524
487
525
### Support
488
526
489
527
File bug reports, feature requests and questions using [GitHub libcoro Issues](https://github.com/jbaldwin/libcoro/issues)
-[coro::thread_pool](#thread_pool) for coroutine cooperative multitasking
29
29
-[coro::io_scheduler](#io_scheduler) for driving i/o events
30
-
- Can use coro::thread_pool for latency sensitive or long lived tasks.
30
+
- Can use `coro::thread_pool` for latency sensitive or long lived tasks.
31
31
- Can use inline task processing for thread per core or short lived tasks.
32
-
- Currently uses an epoll driver
32
+
- Currently uses an epoll driver, only supported on linux.
33
33
-[coro::task_container](#task_container) for dynamic task lifetimes
34
34
* Coroutine Networking
35
35
- coro::net::dns::resolver for async dns
36
36
- Uses libc-ares
37
37
-[coro::net::tcp::client](#io_scheduler)
38
38
-[coro::net::tcp::server](#io_scheduler)
39
+
*[Example TCP/HTTP Echo Server](#tcp_echo_server)
39
40
- coro::net::tls::client (OpenSSL)
40
41
- coro::net::tls::server (OpenSSL)
41
42
- coro::net::udp::peer
42
-
*[Example TCP/HTTP Echo Server](#tcp_echo_server)
43
-
43
+
*
44
44
*[Requirements](#requirements)
45
45
*[Build Instructions](#build-instructions)
46
-
*[Testing](#tests)
46
+
*[Contributing](#contributing)
47
47
*[Support](#support)
48
48
49
49
## Usage
50
50
51
-
### A note on co_await
52
-
Its important to note with coroutines that depending on the construct used _any_`co_await` has the potential to switch the thread that is executing the currently running coroutine. In general this shouldn't affect the way any user of the library would write code except for `thread_local`. Usage of `thread_local` should be extremely careful and _never_ used across any `co_await` boundary do to thread switching and work stealing on thread pools.
51
+
### A note on co_await and threads
52
+
Its important to note with coroutines that _any_`co_await` has the potential to switch the underyling thread that is executing the currently executing coroutine if the scheduler used has more than 1 thread. In general this shouldn't affect the way any user of the library would write code except for `thread_local`. Usage of `thread_local` should be extremely careful and _never_ used across any `co_await` boundary do to thread switching and work stealing on libcoro's schedulers. The only way this is safe is by using a `coro::thread_pool` with 1 thread or an inline `io_scheduler` which also only has 1 thread.
53
+
54
+
### sync_wait
55
+
The `sync_wait` construct is meant to be used outside of a coroutine context to block the calling thread until the coroutine has completed. The coroutine can be executed on the calling thread or scheduled on one of libcoro's schedulers.
56
+
57
+
```C++
58
+
#include<coro/coro.hpp>
59
+
#include<iostream>
60
+
61
+
intmain()
62
+
{
63
+
// This lambda will create a coro::task that returns a unit64_t.
64
+
// It can be invoked many times with different arguments.
65
+
auto make_task_inline = [](uint64_t x) -> coro::task<uint64_t> { co_return x + x; };
66
+
67
+
// This will block the calling thread until the created task completes.
68
+
// Since this task isn't scheduled on any coro::thread_pool or coro::io_scheduler
69
+
// it will execute directly on the calling thread.
70
+
auto result = coro::sync_wait(make_task_inline(5));
71
+
std::cout << "Inline Result = " << result << "\n";
72
+
73
+
// We'll make a 1 thread coro::thread_pool to demonstrate offloading the task's
74
+
// execution to another thread. We'll capture the thread pool in the lambda,
75
+
// note that you will need to guarantee the thread pool outlives the coroutine.
auto make_task_offload = [&tp](uint64_t x) -> coro::task<uint64_t>
79
+
{
80
+
co_await tp.schedule(); // Schedules execution on the thread pool.
81
+
co_return x + x; // This will execute on the thread pool.
82
+
};
83
+
84
+
// This will still block the calling thread, but it will now offload to the
85
+
// coro::thread_pool since the coroutine task is immediately scheduled.
86
+
result = coro::sync_wait(make_task_offload(10));
87
+
std::cout << "Offload Result = " << result << "\n";
88
+
}
89
+
```
90
+
91
+
Expected output:
92
+
```bash
93
+
$ ./examples/coro_sync_wait
94
+
Inline Result = 10
95
+
Offload Result = 20
96
+
```
97
+
98
+
### when_all
99
+
The `when_all` construct can be used within coroutines to await a set of tasks, or it can be used outside coroutinne context in conjunction with `sync_wait` to await multiple tasks. Each task passed into `when_all` will initially be executed serially by the calling thread so it is recommended to offload the tasks onto a scheduler like `coro::thread_pool` or `coro::io_scheduler` so they can execute in parallel.
100
+
101
+
```C++
102
+
#include<coro/coro.hpp>
103
+
#include<iostream>
104
+
105
+
intmain()
106
+
{
107
+
// Create a thread pool to execute all the tasks in parallel.
// Create the task we want to invoke multiple times and execute in parallel on the thread pool.
110
+
auto twice = [&](uint64_t x) -> coro::task<uint64_t>
111
+
{
112
+
co_await tp.schedule(); // Schedule onto the thread pool.
113
+
co_return x + x; // Executed on the thread pool.
114
+
};
115
+
116
+
// Make our tasks to execute, tasks can be passed in via a std::ranges::range type or var args.
117
+
std::vector<coro::task<uint64_t>> tasks{};
118
+
for (std::size_t i = 0; i < 5; ++i)
119
+
{
120
+
tasks.emplace_back(twice(i + 1));
121
+
}
122
+
123
+
// Synchronously wait on this thread for the thread pool to finish executing all the tasks in parallel.
124
+
auto results = coro::sync_wait(coro::when_all(std::move(tasks)));
125
+
for (auto& result : results)
126
+
{
127
+
// If your task can throw calling return_value() will either return the result or re-throw the exception.
128
+
try
129
+
{
130
+
std::cout << result.return_value() << "\n";
131
+
}
132
+
catch (const std::exception& e)
133
+
{
134
+
std::cerr << e.what() << '\n';
135
+
}
136
+
}
137
+
138
+
// Use var args instead of a container as input to coro::when_all.
139
+
auto square = [&](double x) -> coro::task<double>
140
+
{
141
+
co_await tp.schedule();
142
+
co_return x* x;
143
+
};
144
+
145
+
// Var args allows you to pass in tasks with different return types and returns
146
+
// the result as a std::tuple.
147
+
auto tuple_results = coro::sync_wait(coro::when_all(square(1.1), twice(10)));
148
+
149
+
auto first = std::get<0>(tuple_results).return_value();
150
+
auto second = std::get<1>(tuple_results).return_value();
151
+
152
+
std::cout << "first: " << first << " second: " << second << "\n";
153
+
}
154
+
```
155
+
156
+
Expected output:
157
+
```bash
158
+
$ ./examples/coro_when_all
159
+
2
160
+
4
161
+
6
162
+
8
163
+
10
164
+
first: 1.21 second: 20
165
+
```
53
166
54
167
### task
55
-
The `coro::task<T>` is the main coroutine building block within `libcoro`. Use task to create your coroutines and `co_await` or `co_yield` tasks within tasks to perform asynchronous operations, lazily evaluation or even spreading work out across a `coro::thread_pool`. Tasks are lightweight and only begin execution upon awaiting them. If their return type is not `void` then the value can be returned by const reference or by moving (r-value reference).
168
+
The `coro::task<T>` is the main coroutine building block within `libcoro`. Use task to create your coroutines and `co_await` or `co_yield` tasks within tasks to perform asynchronous operations, lazily evaluation or even spreading work out across a `coro::thread_pool`. Tasks are lightweight and only begin execution upon awaiting them.
56
169
57
170
58
171
```C++
@@ -62,11 +175,12 @@ The `coro::task<T>` is the main coroutine building block within `libcoro`. Use
// Create a very large object and return it by moving the value so the
109
223
// contents do not have to be copied out.
110
-
auto move_output_task = []() -> coro::task<expensive_struct> {
224
+
auto move_output_task = []() -> coro::task<expensive_struct>
225
+
{
111
226
expensive_struct data{};
112
227
data.id = "12345678-1234-5678-9012-123456781234";
113
228
for (size_t i = 10'000; i < 100'000; ++i)
@@ -1177,21 +1292,27 @@ target_link_libraries(${PROJECT_NAME} PUBLIC libcoro)
1177
1292
##### Package managers
1178
1293
libcoro is available via package managers [Conan](https://conan.io/center/libcoro) and [vcpkg](https://vcpkg.io/).
1179
1294
1295
+
### Contributing
1296
+
1297
+
Contributing is welcome, if you have ideas or bugs please open an issue. If you want to open a PR they are also welcome, if you are adding a bugfix or a feature please include tests to verify the feature or bugfix is working properly. If it isn't included I will be asking for you to add some!
1298
+
1180
1299
#### Tests
1181
-
The tests will automatically be run by github actions on creating a pull request. They can also be ran locally:
1300
+
The tests will automatically be run by github actions on creating a pull request. They can also be ran locally after building from the build directory:
1182
1301
1183
1302
# Invoke via cmake with all output from the tests displayed to console:
1184
1303
ctest -VV
1185
1304
1186
1305
# Or invoke directly, can pass the name of tests to execute, the framework used is catch2.
1187
-
# Tests are tagged with their group, below is howt o run all of the coro::net::tcp::server tests:
1188
-
./Debug/test/libcoro_test "[tcp_server]"
1306
+
# Tests are tagged with their group, below is how to run all of the coro::net::tcp::server tests:
1307
+
./test/libcoro_test "[tcp_server]"
1308
+
1309
+
If you open a PR for a bugfix or new feature please include tests to verify that the change is working as intended. If your PR doesn't include tests I will ask you to add them and won't merge until they are added and working properly. Tests are found in the `/test` directory and are organized by object type.
1189
1310
1190
1311
### Support
1191
1312
1192
1313
File bug reports, feature requests and questions using [GitHub libcoro Issues](https://github.com/jbaldwin/libcoro/issues)
0 commit comments