Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,6 @@ FetchContent_Declare(
GIT_TAG 330be95
)
FetchContent_MakeAvailable(execution)
FetchContent_Declare(
net
# SOURCE_DIR <path-to>/net
GIT_REPOSITORY https://github.com/bemanproject/net
GIT_TAG ea1fd8f
)
if(NOT WIN32)
FetchContent_MakeAvailable(net)
endif()

add_subdirectory(src/beman/task)

Expand Down
184 changes: 171 additions & 13 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

<details>
<summary>
[`c++now-affinity.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-affinity.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/8qEG5x7sz): demo scheduler affinity
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-affinity.cpp'>c++now-affinity.cpp</a>
<a href='https://godbolt.org/z/8qEG5x7sz'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo scheduler affinity
</summary>

The example program
[`c++now-affinity.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-affinity.cpp)
[![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/8qEG5x7sz) uses
[`demo::thread_loop`](../examples/demo-thread_loop.hpp) to demonstrate
uses [`demo::thread_loop`](../examples/demo-thread_loop.hpp) to demonstrate
the behavior of _scheduler affinity_: the idea is that scheduler
affinity causes the coroutine to resume on the same scheduler as
the one the coroutine was started on. The program implements three
Expand Down Expand Up @@ -56,21 +57,178 @@ the `inline_scheduler` doesn't do any actual scheduling.

</details>

- [`c++now-allocator.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-allocator.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/719v7en6a)
- [`c++now-basic.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-basic.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/7Pn5TEhfK)
- [`c++now-cancel.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-cancel.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/vx4PqYvE6)
- [`c++now-errors.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-errors.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/95Mhr5MGn)
- [`c++now-query.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-query.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/dPboEeqfv)
- [`c++now-result-types.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-result-types.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/aWfc8T8he)
- [`c++now-return.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-return.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/f5YE5W4Ta)
- [`c++now-stop_token.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-stop_token.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/TxYe3jEs7)
- [`c++now-with_error.cpp`](https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-with_error.cpp) [![Compiler Explorer](compiler-explorer.ico)](https://godbolt.org/z/6oqox6zf8)
<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-allocator.cpp'><code>c++now-allocator.cpp</code></a>
<a href='https://godbolt.org/z/719v7en6a'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo allocator use
</summary>

This demo shows how to configure `task`'s environment argument to
use a different allocator than the default `std::allocator<std::byte>`.
To do so it defines an environment type `with_allocator` which
defines a nested type alias `allocator_type` to be
`std::pmr::polymorphic_allocator<std::byte>`.

The coroutine `coro` shows how to use `read_env` to extract the
used allocator object to potentially use it for any allocation
purposes within the coroutine. There are two uses of `coro`, the
first one using the default which just uses
`std::pmr::polymorphic_allocator<std::byte>()` to allocate memory.
The second use explicitly specifies the memory resource
`std::pmr::new_delete_resource()` to initialized the use
`std::pmr::polymorphic_allocator<std::byte>`.

</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-basic.cpp'><code>c++now-basic.cpp</code></a>
<a href='https://godbolt.org/z/7Pn5TEhfK'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo basic <code>task</code> use
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-basic.cpp'><code>c++now-basic.cpp</code></a>
shows some basic use of a `task`:

1. The coroutine `basic` just `co_await`s the awaiter `std::suspend_never{}` which immediately completes.
This use demonstrates that any awaiter can be `co_await`ed by a `task<...>`.
2. The coroutine `await_sender` demonstrates the results of `co_await`ing various senders. It uses variations of
`just*` to show the different results:
- `co_await`ing a sender completing with `set_value_t()`, e.g., `just()`, produces an expression with type `void`.
- `co_await`ing a sender completing with `set_value_t(T)`, e.g., `just(1)`, produces an expression with type `T`.
- `co_await`ing a sender completing with <code>set_value_t(T<sub>0</sub>, ..., T<sub>n</sub>)</code>, e.g., `just(1, true)`, produces an expression with type <code>tuple&lt;T<sub>0</sub>, ..., T<sub>n</sub>&gt;</code>.
- `co_await`ing a sender completing with `set_error_t(E)`, e.g., `just_error(1)`, results in an exception of type `E` being thrown.
- `co_await`ing a sender completing with `set_stopped_t()`, e.g., `just_stopped()`, results in the corouting never getting resumed although all local objects are properly destroyed.
</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-cancel.cpp'><code>c++now-cancel.cpp</code></a>
<a href='https://godbolt.org/z/vx4PqYvE6'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo how a <code>task</code> can actively cancel the work
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-cancel.cpp'><code>c++now-cancel.cpp</code></a>
shows a coroutine `co_await`ing `just_stopped()` which results in the coroutine getting cancelled. The coroutine will
complete with `set_stopped()`.
</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-errors.cpp'><code>c++now-errors.cpp</code></a>
<a href='https://godbolt.org/z/95Mhr5MGn'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo how to handle errors within <code>task</code>
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-errors.cpp'><code>c++now-errors.cpp</code></a>
shows examples of how to handle errors within a coroutine:

- The coroutine `error_result` simply `co_await`s a sender producing an error (`just_error(17)`). When
a `co_await`ed sender completes with `set_error_t(T)` an exception of type `T` is thrown and the error
needs to be handled with a `try`/`catch` block. Otherwise the coroutine itself completes with `set_error_t(exception_ptr)`
where the `exception_ptr` hold the thrown exception object.
- The coroutine `expected` uses a sender algorithm `as_expected` which is implemented at the top of the example
to turn the result of the `co_await`ed sender into an object of type `expected<T, E>`, avoiding an exception
from being thrown.

</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-query.cpp'><code>c++now-query.cpp</code></a>
<a href='https://godbolt.org/z/dPboEeqfv'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo passing a custom envrionment into a <code>task</code>
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-query.cpp'><code>c++now-query.cpp</code></a>
shows how to define and use a custom environment element.

1. The coroutine `with_env` uses a simple environment named `context` which just defines a custom query for `get_value`
to obtain a value. The value itself gets initialized from the environment of the receiver used with the `task`.
2. The coroutine `with_fancy_env` uses an environment which embed a an object depending the type of the environment
of the receiver used with the `task`. While the type accessed from within the `task` needs to be type-erased,
the actually stored value can depend on the environment of the upstream receiver.
</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-result-types.cpp'><code>c++now-result-types.cpp</code></a>
<a href='https://godbolt.org/z/aWfc8T8he'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo the result type of <code>co_await</code> expressions.
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-result-types.cpp'><code>c++now-result-types.cpp</code></a>
shows the result types of successful senders using variatons of `just`:

- `co_await just()` doesn't produce a value, i.e., the type of the expression is `void`.
- `co_await just(1)` produces an `int`.
- `co_await just(1, true)` produces a `tuple<int, bool>`.

</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-return.cpp'><code>c++now-return.cpp</code></a>
<a href='https://godbolt.org/z/f5YE5W4Ta'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
shows the different normal values returned
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-return.cpp'><code>c++now-return.cpp</code></a>
shows various ways of returning normally (without an error) for a `task`. Some of the coroutines are set up to produce
specific error results although none of them are actually use:

- `default_return` shows that the default return type for `task<>` is `void`.
- `void_return` explicitly specifies a `void` return type.
- `int_return` specifies the return type as `int` and returns an `int` value.
- `error_return` specifies the return type as `int` and also specifies custom error results.
- `no_error_return` specifies the return type as `int` and also specifies that the coroutine can't produce any error.
</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-stop_token.cpp'><code>c++now-stop_token.cpp</code></a>
<a href='https://godbolt.org/z/TxYe3jEs7'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo how to get and use a stop token in a `task`
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-stop_token.cpp'><code>c++now-stop_token.cpp</code></a>
shows how to get a stop token in side a `task` and how to use it to cancel active work. It doesn't actually complete
with a `set_stopped()` but completes with `set_value()`.

- In the coroutine `co_await read_env(get_stop_token)` is used to get a stop token.
- In the loop the value of `token.stop_requested()` is checked to determine if the loop should continue.
- In `main` an `inplace_stop_source` is used to have something which can be stopped.
- When running the coroutine `stopping` on a separate thread, the environment is changed using `write_env` to use stop token from `main`'s stop source in the environment.
- After sleeping for a bit, `source.request_stop()` is called to trigger cancellation of the coroutine.
</details>

<details>
<summary>
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-with_error.cpp'><code>c++now-with_error.cpp</code></a>
<a href='https://godbolt.org/z/6oqox6zf8'><img src='https://raw.githubusercontent.com/bemanproject/task/refs/heads/main/docs/compiler-explorer.ico' width='15' height='15'/></a>:
demo exiting a <code>task</code> with an error
</summary>

The example
<a href='https://github.com/bemanproject/task/blob/main/examples/c%2B%2Bnow-with_error.cpp'><code>c++now-with_error.cpp</code></a>
shows how a coroutine can be exited reporting an error without throwing an exception. To do so, the coroutine
uses `co_yield with_error(e)`. By default the `task` only declares `set_error_t(exception_ptr)`. To return
other errors, an environment declarating a suitable `set_error_t(E)` completion using the `error_types` alias is used.
</details>

## Tools Used By The Examples

<details>
<summary>
[`demo::thread_loop`](../examples/demo-thread_loop.hpp) is a `run_loop` whose `run()` is called from a `std::thread`.
<a href='https://github.com/bemanproject/task/blob/remove-net-and-improve-docs/examples/demo-thread_loop.hpp'><code>demo::thread_loop</code> is a <code>run_loop</code> whose <code>run()</code> is called from a <code>std::thread</code>.
</summary>

Technically [`demo::thread_loop`](../examples/demo-thread_loop.hpp)
Expand Down
11 changes: 1 addition & 10 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ set(ALL_EXAMPLES
co_await-result
co_await-task
container
environment
error
escaped-exception
friendly
Expand All @@ -42,15 +41,7 @@ message("Examples to be built: ${ALL_EXAMPLES}")
foreach(example ${ALL_EXAMPLES})
add_executable(beman.task.examples.${example})
target_sources(beman.task.examples.${example} PRIVATE ${example}.cpp)
if(WIN32)
target_link_libraries(beman.task.examples.${example} beman::task)
else()
target_link_libraries(
beman.task.examples.${example}
beman::task
beman::net
)
endif()
target_link_libraries(beman.task.examples.${example} beman::task)
add_test(
NAME beman.task.examples.${example}
COMMAND $<TARGET_FILE:beman.task.examples.${example}>
Expand Down
4 changes: 2 additions & 2 deletions examples/c++now-errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ ex::task<> error_result() {

#if 202202 <= __cpp_lib_expected
ex::task<> expected() {
[[maybe_unused]] auto e = co_await as_expected(ex::just(17));
auto e = co_await as_expected(ex::just(17));
print_expected("expected with value=", e);
[[maybe_unused]] auto u = co_await as_expected(ex::just_error(17));
auto u = co_await as_expected(ex::just_error(17));
print_expected("expected without value=", u);
}
#endif
Expand Down
7 changes: 5 additions & 2 deletions examples/c++now-query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ struct context {
int query(const get_value_t&) const { return this->value; }
};

ex::task<void, context> with_env() { [[maybe_unused]] decltype(auto) v = co_await ex::read_env(get_value); }
ex::task<void, context> with_env() {
decltype(auto) v = co_await ex::read_env(get_value);
std::cout << "with_env: v=" << v << "\n";
}

struct fancy {
struct env_base {
Expand All @@ -52,7 +55,7 @@ struct fancy {
};

ex::task<void, fancy> with_fancy_env() {
[[maybe_unused]] decltype(auto) v = co_await ex::read_env(get_value);
decltype(auto) v = co_await ex::read_env(get_value);
std::cout << "v=" << v << "\n";
}
} // namespace
Expand Down
3 changes: 0 additions & 3 deletions examples/demo-thread_loop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
// ----------------------------------------------------------------------------

#include <beman/execution/execution.hpp>
#include <condition_variable>
#include <exception>
#include <functional>
#include <mutex>
#include <thread>

namespace demo {
Expand Down
Loading