Skip to content
423 changes: 423 additions & 0 deletions docs/create-a-sender.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ endif()
set(TODO suspend_never) #-dk:TODO including that causes ASAN errors

set(EXAMPLES
tutorial-create-a-sender
allocator
doc_just
doc_just_error
Expand All @@ -35,6 +36,9 @@ set(EXAMPLES
stop_token
when_all_cancel
)
set(xEXAMPLES
tutorial-create-a-sender
)
Comment on lines +39 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pre-commit] reported by reviewdog 🐶

Suggested change
set(xEXAMPLES
tutorial-create-a-sender
)
set(xEXAMPLES tutorial-create-a-sender)


if(BEMAN_USE_MODULES)
#-dk:TODO gcc doesn't like the modules: list(APPEND EXAMPLES modules modules_and_header)
Expand Down
198 changes: 198 additions & 0 deletions examples/tutorial-create-a-sender.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// examples/tutorial/create-a-sender.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <iostream>
#include <optional>
#include <stack>
#ifdef BEMAN_HAS_MODULES
import beman.execution;
import beman.execution.detail;
#else
#include <beman/execution/execution.hpp>
#endif

namespace ex = beman::execution;

namespace {
template <typename T>
class asynchronous_stack {
struct node {
node* next{};
virtual void complete(T) = 0;
};
std::stack<T> stack;
node* awaiting{};

template <ex::receiver Rcvr>
struct state : node {
using operation_state_concept = ex::operation_state_tag;
struct stop_fun {
state& st;
void operator()() noexcept {
std::cout << "stop callback start\n";
state& s = this->st;
this->st.callback.reset();
for (auto it{&this->st.self.awaiting}; it; it = &(*it)->next) {
if (*it == &this->st) {
*it = this->st.next;
break;
}
}
std::cout << "stop callback completing\n";
ex::set_stopped(std::move(s.rcvr));
std::cout << "stop callback done\n";
}
};
using stop_token_t = ex::stop_token_of_t<decltype(ex::get_env(std::declval<Rcvr&>()))>;
using callback_t = ex::stop_callback_for_t<stop_token_t, stop_fun>;
std::remove_cvref_t<Rcvr> rcvr;
asynchronous_stack& self;
std::optional<callback_t> callback;
state(Rcvr&& r, asynchronous_stack& s) : rcvr(std::forward<Rcvr>(r)), self(s) {}
void start() & noexcept {
if (not this->self.stack.empty()) {
T value(std::move(this->self.stack.top()));
this->self.stack.pop();
ex::set_value(std::move(rcvr), std::move(value));
} else {
this->next = std::exchange(this->self.awaiting, this);
this->callback.emplace(ex::get_stop_token(ex::get_env(this->rcvr)), stop_fun{*this});
}
}
void complete(T value) override {
this->callback.reset();
ex::set_value(std::move(rcvr), std::move(value));
}
};

public:
struct pop_sender {
using sender_concept = ex::sender_tag;
template <typename...>
static consteval auto get_completion_signatures() {
return ex::completion_signatures<ex::set_value_t(T), ex::set_stopped_t()>{};
}

asynchronous_stack& self;
template <ex::receiver Rcvr>
auto connect(Rcvr&& rcvr) const {
static_assert(ex::operation_state<state<Rcvr>>);
return state<Rcvr>{std::forward<Rcvr>(rcvr), self};
}
};

void push(T value) {
if (this->awaiting) {
std::exchange(this->awaiting, this->awaiting->next)->complete(std::move(value));
} else {
this->stack.push(std::move(value));
}
}
pop_sender pop() { return pop_sender{*this}; }
};

static_assert(ex::sender<asynchronous_stack<int>::pop_sender>);
static_assert(ex::sender_in<asynchronous_stack<int>::pop_sender>);

struct stop_test {
using sender_concept = ex::sender_tag;
template <typename...>
static consteval auto get_completion_signatures() {
return ex::completion_signatures<ex::set_value_t()>();
}
template <typename Rcvr>
struct state {
using operation_state_concept = ex::operation_state_tag;

struct cb {
state* self;
auto operator()() const noexcept -> void {
std::cout << "cb\n";
ex::set_value(std::move(self->rcvr));
}
};
using callback = ex::stop_callback_for_t<ex::stop_token_of_t<ex::env_of_t<Rcvr>>, cb>;

std::remove_cvref_t<Rcvr> rcvr;
std::optional<callback> callb;
auto start() & noexcept -> void {
std::cout << "stop_test start\n";
this->callb.emplace(ex::get_stop_token(ex::get_env(this->rcvr)), cb{this});
}
};
template <typename Rcvr>
state<Rcvr> connect(Rcvr&& rcvr) const {
return {std::forward<Rcvr>(rcvr)};
}
};
} // namespace
// ----------------------------------------------------------------------------

int main() {
std::cout << std::unitbuf;
#if 1
asynchronous_stack<int> st;
ex::counting_scope scope;
[[maybe_unused]] auto sender = st.pop() | ex::then([](int v) { std::cout << "got value=" << v << "\n"; });

#if 1
for (int value{1}; value < 4; ++value) {
st.push(value);
}
std::cout << "pushed 1,2,3\n";

int count{8};
for (int value{1}; value < count; ++value) {
ex::spawn(st.pop() | ex::then([value](int v) noexcept {
std::cout << "got value=" << v << " for request " << value << "\n";
}) | ex::upon_stopped([value] noexcept { std::cout << "request " << value << " was stopped\n"; }),
scope.get_token());
}

std::cout << "requested " << (count - 1) << " values\n";

for (int value{4}; value < 7; ++value) {
st.push(value);
}
std::cout << "pushed 4,5,6\n";
#endif

std::cout << "requesting stop\n";
scope.request_stop();
std::cout << "requested stop\n";
ex::sync_wait(scope.join() | ex::then([] { std::cout << "joined\n"; }));
std::cout << "joined\n";
#else

std::optional<ex::inplace_stop_source> source1{};
source1.emplace();
std::optional<ex::inplace_stop_source> source2{};
source2.emplace();
struct receiver {
using receiver_concept = ex::receiver_tag;
std::optional<ex::inplace_stop_source>& source1;
std::optional<ex::inplace_stop_source>& source2;
auto query(ex::get_stop_token_t) const noexcept { return this->source2->get_token(); }
auto get_env() const noexcept {
std::cout << "get_env\n";
return *this;
}
auto set_value() noexcept {
std::cout << "receiver::set_value\n";
source1.reset();
std::cout << "receiver::set_value done\n";
}
auto set_stopped() noexcept { std::cout << "receiver::set_stopped\n"; }
};
#if 1
auto sws(ex::connect(ex::detail::stop_when(stop_test(), source1->get_token()), receiver{source1, source2}));
#else
auto sws(ex::connect(stop_test(), receiver{source1, source2}));
#endif
std::cout << "start\n";
ex::start(sws);
std::cout << "request stop\n";
source1->request_stop();
std::cout << "requested stop\n";
#endif
}
6 changes: 3 additions & 3 deletions include/beman/execution/detail/call_result_t.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/call_result_t.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_CALL_RESULT
#define INCLUDED_BEMAN_EXECUTION_DETAIL_CALL_RESULT
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_CALL_RESULT_T
#define INCLUDED_BEMAN_EXECUTION_DETAIL_CALL_RESULT_T

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
Expand All @@ -25,4 +25,4 @@ using call_result_t = decltype(::std::declval<Fun>()(std::declval<Args>()...));

// ----------------------------------------------------------------------------

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_CALL_RESULT
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_CALL_RESULT_T
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/completion_signaturess_of_t.hpp -*-C++-*-
// include/beman/execution/detail/completion_signatures_of_t.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_SIGNATURES_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_SIGNATURES_OF
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_SIGNATURES_OF_T
#define INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_SIGNATURES_OF_T

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_MODULES
Expand Down Expand Up @@ -31,4 +31,4 @@ using completion_signatures_of_t = decltype(::beman::execution::get_completion_s

// ----------------------------------------------------------------------------

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_SIGNATURES_OF
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_SIGNATURES_OF_T
6 changes: 3 additions & 3 deletions include/beman/execution/detail/connect_result_t.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/connect_result_t.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_CONNECT_RESULT
#define INCLUDED_BEMAN_EXECUTION_DETAIL_CONNECT_RESULT
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_CONNECT_RESULT_T
#define INCLUDED_BEMAN_EXECUTION_DETAIL_CONNECT_RESULT_T

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
Expand All @@ -29,4 +29,4 @@ using connect_result_t = decltype(::beman::execution::connect(::std::declval<Sen

// ----------------------------------------------------------------------------

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_CONNECT_RESULT
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_CONNECT_RESULT_T
4 changes: 2 additions & 2 deletions include/beman/execution/detail/dependent_sender_error.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/dependent_sender_error.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_DEPENDENT_SENDER_ERROR
#define INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_DEPENDENT_SENDER_ERROR
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_DEPENDENT_SENDER_ERROR
#define INCLUDED_BEMAN_EXECUTION_DETAIL_DEPENDENT_SENDER_ERROR

// ----------------------------------------------------------------------------

Expand Down
6 changes: 3 additions & 3 deletions include/beman/execution/detail/env_of_t.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/env_of_t.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ENV_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ENV_OF
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ENV_OF_T
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ENV_OF_T

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
Expand All @@ -29,4 +29,4 @@ using env_of_t = decltype(::beman::execution::get_env(::std::declval<T>()));

// ----------------------------------------------------------------------------

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_ENV_OF
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_ENV_OF_T
6 changes: 3 additions & 3 deletions include/beman/execution/detail/error_types_of_t.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/error_types_of_t.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ERROR_TYPES_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ERROR_TYPES_OF
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ERROR_TYPES_OF_T
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ERROR_TYPES_OF_T

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
Expand Down Expand Up @@ -46,4 +46,4 @@ using error_types_of_t =

// ----------------------------------------------------------------------------

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_ERROR_TYPES_OF
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_ERROR_TYPES_OF_T
4 changes: 2 additions & 2 deletions include/beman/execution/detail/get_completion_domain.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/get_completion_domain.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_GET_COMPLETION_DOMAIN
#define INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_GET_COMPLETION_DOMAIN
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_GET_COMPLETION_DOMAIN
#define INCLUDED_BEMAN_EXECUTION_DETAIL_GET_COMPLETION_DOMAIN

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
Expand Down
6 changes: 3 additions & 3 deletions include/beman/execution/detail/hide_sched.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/hide_sched.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_HIDE_SCHED
#define INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_HIDE_SCHED
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_HIDE_SCHED
#define INCLUDED_BEMAN_EXECUTION_DETAIL_HIDE_SCHED

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
Expand Down Expand Up @@ -48,4 +48,4 @@ auto hide_sched(const Q& q) noexcept {

// ----------------------------------------------------------------------------

#endif // INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_HIDE_SCHED
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_HIDE_SCHED
4 changes: 2 additions & 2 deletions include/beman/execution/detail/indeterminate_domain.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/indeterminate_domain.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_INDETERMINATE_DOMAIN
#define INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_INDETERMINATE_DOMAIN
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_INDETERMINATE_DOMAIN
#define INCLUDED_BEMAN_EXECUTION_DETAIL_INDETERMINATE_DOMAIN

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
Expand Down
19 changes: 13 additions & 6 deletions include/beman/execution/detail/inplace_stop_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,25 +139,32 @@ inline auto beman::execution::inplace_stop_source::request_stop() -> bool {
this->running = it;
this->id = ::std::this_thread::get_id();
this->callbacks = it->next;
{
if (this->callbacks) {
relock r(&guard);
guard.unlock();
it->call();
this->running = nullptr;
} else {
guard.unlock();
it->call();
this->running = nullptr;
break;
}
this->running = nullptr;
}
return true;
}
return false;
}

inline auto beman::execution::inplace_stop_source::add(callback_base* cb) -> void {
if (this->stopped) {
cb->call();
} else {
{
::std::lock_guard guard(this->lock);
cb->next = ::std::exchange(this->callbacks, cb);
if (!this->stopped) {
cb->next = ::std::exchange(this->callbacks, cb);
return;
}
}
cb->call();
}

inline auto beman::execution::inplace_stop_source::deregister(callback_base* cb) -> void {
Expand Down
Loading
Loading