Skip to content
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/allocator_of.hpp -*-C++-*-
// include/beman/execution/detail/task/allocator_of.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ALLOCATOR_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ALLOCATOR_OF
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_ALLOCATOR_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_ALLOCATOR_OF

#include <concepts>
#include <cstddef>
Expand Down
149 changes: 149 additions & 0 deletions include/beman/execution/detail/task/allocator_support.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// include/beman/execution/detail/task/allocator_support.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_ALLOCATOR_SUPPORT
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_ALLOCATOR_SUPPORT

#include <concepts>
#include <cstddef>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>

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

namespace beman::execution::detail {
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) allocator_support_allocation_unit {
std::byte bytes[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
};

static_assert(sizeof(allocator_support_allocation_unit) == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
static_assert(alignof(allocator_support_allocation_unit) == __STDCPP_DEFAULT_NEW_ALIGNMENT__);

struct allocator_support_header {
void (*deallocate)(void*, std::size_t) noexcept;
std::size_t count;
std::size_t palloc_offset;
};

template <typename Alloc>
using allocator_support_alloc_t = std::remove_cvref_t<Alloc>;

template <typename Alloc>
using allocator_support_palloc_t = typename std::allocator_traits<
allocator_support_alloc_t<Alloc>>::template rebind_alloc<allocator_support_allocation_unit>;

template <typename Alloc>
concept allocator_support_allocator_like = requires { typename allocator_support_alloc_t<Alloc>::value_type; };

template <typename Allocator, typename Alloc>
concept allocator_support_allocator_arg =
allocator_support_allocator_like<Alloc> && requires(allocator_support_alloc_t<Alloc>& alloc, std::size_t size) {
typename allocator_support_palloc_t<Alloc>;
typename std::allocator_traits<allocator_support_palloc_t<Alloc>>::pointer;
requires std::same_as<typename std::allocator_traits<allocator_support_palloc_t<Alloc>>::pointer,
allocator_support_allocation_unit*>;
requires(alignof(allocator_support_palloc_t<Alloc>) <= alignof(allocator_support_allocation_unit));
allocator_support_palloc_t<Alloc>(alloc);
std::allocator_traits<allocator_support_palloc_t<Alloc>>::allocate(
std::declval<allocator_support_palloc_t<Alloc>&>(), size);
};

/*!
* \brief Utility adding allocator support to type by embedding the allocator
* \headerfile beman/execution/task.hpp <beman/execution/task.hpp>
*
* To add allocator support using this class just publicly inherit from
* allocator_support<Allocator, YourPromiseType>. This utility is probably
* only useful for coroutine promise types.
*
* This struct is a massive hack, primarily support allocators for coroutines.
* The memory for coroutines is implicitly managed and there isn't a way to
* provide the memory directly. Instead, the promise_type can overload an
* operator new and use a leading std::allocator_arg/allocator pair when it
* is present. Even worse, the operator delete only gets passed a pointer to
* delete and a size. To determine the correct allocator the operator delete
* stores a type-erased deallocation header and the rebound allocator in the
* allocation block.
*/
template <typename Allocator>
struct allocator_support {
static std::size_t align_up(std::size_t value, std::size_t alignment) {
return ((value + alignment - 1u) / alignment) * alignment;
}

static std::size_t header_offset(std::size_t size) {
return allocator_support::align_up(size, alignof(allocator_support_header));
}

static allocator_support_header* get_header(void* ptr, std::size_t size) {
ptr = static_cast<std::byte*>(ptr) + allocator_support::header_offset(size);
return ::std::launder(reinterpret_cast<allocator_support_header*>(ptr));
}

template <typename PAlloc>
static void deallocate_with(void* ptr, std::size_t size) noexcept {
using palloc_traits = std::allocator_traits<PAlloc>;

allocator_support_header* header{allocator_support::get_header(ptr, size)};
auto* palloc_ptr{
::std::launder(reinterpret_cast<PAlloc*>(static_cast<std::byte*>(ptr) + header->palloc_offset))};
PAlloc palloc{*palloc_ptr};
std::size_t count{header->count};
palloc_ptr->~PAlloc();
palloc_traits::deallocate(palloc, static_cast<allocator_support_allocation_unit*>(ptr), count);
}

template <typename Alloc>
requires allocator_support_allocator_arg<Allocator, Alloc>
static void* allocate(std::size_t size, Alloc alloc) {
using palloc_t = allocator_support_palloc_t<Alloc>;
using palloc_traits = std::allocator_traits<palloc_t>;

palloc_t palloc{alloc};
std::size_t header_offset{allocator_support::header_offset(size)};
std::size_t palloc_offset{
allocator_support::align_up(header_offset + sizeof(allocator_support_header), alignof(palloc_t))};
std::size_t count{(palloc_offset + sizeof(palloc_t) + sizeof(allocator_support_allocation_unit) - 1u) /
sizeof(allocator_support_allocation_unit)};

allocator_support_allocation_unit* ptr{palloc_traits::allocate(palloc, count)};
try {
new (static_cast<std::byte*>(static_cast<void*>(ptr)) + header_offset)
allocator_support_header{&allocator_support::deallocate_with<palloc_t>, count, palloc_offset};
new (static_cast<std::byte*>(static_cast<void*>(ptr)) + palloc_offset) palloc_t(palloc);
} catch (...) {
palloc_traits::deallocate(palloc, ptr, count);
throw;
}
return ptr;
}

static void* operator new(std::size_t size) { return allocator_support::allocate(size, Allocator{}); }

template <typename Alloc, typename... A>
requires allocator_support_allocator_arg<Allocator, Alloc>
static void* operator new(std::size_t size, std::allocator_arg_t, Alloc alloc, A&&...) {
return allocator_support::allocate(size, alloc);
}

template <typename This, typename Alloc, typename... A>
requires allocator_support_allocator_arg<Allocator, Alloc>
static void* operator new(std::size_t size, const This&, std::allocator_arg_t, Alloc alloc, A&&...) {
return allocator_support::allocate(size, alloc);
}

template <typename... A>
static void operator delete(void* ptr, std::size_t size, const A&...) noexcept {
allocator_support::operator delete(ptr, size);
}
static void operator delete(void* ptr, std::size_t size) noexcept {
allocator_support::get_header(ptr, size)->deallocate(ptr, size);
}
};
} // namespace beman::execution::detail

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

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

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION
#define INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_COMPLETION
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_COMPLETION

#include <beman/execution/execution.hpp>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/error_types_of.hpp -*-C++-*-
// include/beman/execution/detail/task/error_types_of.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_CONTEXT_ERROR_TYPES_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_CONTEXT_ERROR_TYPES_OF
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_ERROR_TYPES_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_ERROR_TYPES_OF

#include <beman/execution/execution.hpp>
#include <exception>
Expand All @@ -25,4 +25,4 @@ using error_types_of_t = typename error_types_of<Context>::type;

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

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_CONTEXT_ERROR_TYPES_OF
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_ERROR_TYPES_OF
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment on the trailing #endif seems unusual. Maybe instead of fixing it remove it? In theory these comments should be added/removed by a code formatting tool like clang-format (I don't know if that actually does such transformations).

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/find_allocator.hpp -*-C++-*-
// include/beman/execution/detail/task/find_allocator.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_FIND_ALLOCATOR
#define INCLUDED_BEMAN_EXECUTION_DETAIL_FIND_ALLOCATOR
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_FIND_ALLOCATOR
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_FIND_ALLOCATOR

#include <concepts>
#include <memory>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/handle.hpp -*-C++-*-
// include/beman/execution/detail/task/handle.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_HANDLE
#define INCLUDED_BEMAN_EXECUTION_DETAIL_HANDLE
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_HANDLE
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_HANDLE

#include <beman/execution/execution.hpp>
#include <cassert>
Expand Down
58 changes: 58 additions & 0 deletions include/beman/execution/detail/task/infallible_scheduler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// include/beman/execution/detail/task/infallible_scheduler.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_INFALLIBLE_SCHEDULER
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_INFALLIBLE_SCHEDULER

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
import std;
#else
#include <concepts>
#include <utility>
#endif
#ifdef BEMAN_HAS_MODULES
import beman.execution.detail.completion_signatures;
import beman.execution.detail.completion_signatures_of_t;
import beman.execution.detail.env;
import beman.execution.detail.schedule;
import beman.execution.detail.scheduler;
import beman.execution.detail.set_stopped;
import beman.execution.detail.set_value;
import beman.execution.detail.stop_token_of_t;
import beman.execution.detail.unstoppable_token;
#else
#include <beman/execution/detail/completion_signatures.hpp>
#include <beman/execution/detail/completion_signatures_of_t.hpp>
#include <beman/execution/detail/env.hpp>
#include <beman/execution/detail/schedule.hpp>
#include <beman/execution/detail/scheduler.hpp>
#include <beman/execution/detail/set_stopped.hpp>
#include <beman/execution/detail/set_value.hpp>
#include <beman/execution/detail/stop_token_of_t.hpp>
#include <beman/execution/detail/unstoppable_token.hpp>
#endif

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

namespace beman::execution::detail {
template <class Sch, class Env, class... Comp>
concept completes_with =
::std::same_as<::beman::execution::completion_signatures<Comp...>,
::beman::execution::
completion_signatures_of_t<decltype(::beman::execution::schedule(::std::declval<Sch>())), Env>>;

template <class Sch, class Env>
concept infallible_scheduler =
(::beman::execution::scheduler<Sch>) &&
(::beman::execution::detail::completes_with<Sch, Env, ::beman::execution::set_value_t()> ||
(!::beman::execution::unstoppable_token<::beman::execution::stop_token_of_t<Env>> &&
(::beman::execution::detail::
completes_with<Sch, Env, ::beman::execution::set_value_t(), ::beman::execution::set_stopped_t()> ||
::beman::execution::detail::
completes_with<Sch, Env, ::beman::execution::set_stopped_t(), ::beman::execution::set_value_t()>)));
} // namespace beman::execution::detail

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

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

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_LOGGER
#define INCLUDED_BEMAN_EXECUTION_DETAIL_LOGGER
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_LOGGER
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_LOGGER

#include <algorithm>
#include <iterator>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// include/beman/execution/detail/poly.hpp -*-C++-*-
// include/beman/execution/detail/task/poly.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_POLY
#define INCLUDED_BEMAN_EXECUTION_DETAIL_POLY
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_POLY
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_POLY

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
import std;
#else
#include <array>
#include <concepts>
#include <cstddef>
#include <new>
#include <utility>
#endif

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/promise_env.hpp -*-C++-*-
// include/beman/execution/detail/task/promise_env.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_PROMISE_ENV
#define INCLUDED_BEMAN_EXECUTION_DETAIL_PROMISE_ENV
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_PROMISE_ENV
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_PROMISE_ENV

#include <beman/execution/execution.hpp>
#include <utility>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/state_rep.hpp -*-C++-*-
// include/beman/execution/detail/task/state_rep.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_STATE_REP
#define INCLUDED_BEMAN_EXECUTION_DETAIL_STATE_REP
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_STATE_REP
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_STATE_REP

#include <beman/execution/execution.hpp>
#include <type_traits>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// include/beman/execution/detail/stop_source_of.hpp -*-C++-*-
// include/beman/execution/detail/task/stop_source.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_STOP_SOURCE_OF
#define INCLUDED_BEMAN_EXECUTION_DETAIL_STOP_SOURCE_OF
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_STOP_SOURCE
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_STOP_SOURCE

#include <beman/execution/stop_token.hpp>

Expand All @@ -24,4 +24,4 @@ using stop_source_of_t = typename stop_source_of<Context>::type;

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

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_STOP_SOURCE_OF
#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_STOP_SOURCE
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// include/beman/execution/detail/sub_visit.hpp -*-C++-*-
// include/beman/execution/detail/task/sub_visit.hpp -*-C++-*-
// ----------------------------------------------------------------------------
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_SUB_VISIT
#define INCLUDED_BEMAN_EXECUTION_DETAIL_SUB_VISIT
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_SUB_VISIT
#define INCLUDED_BEMAN_EXECUTION_DETAIL_TASK_SUB_VISIT

#include <utility>
#include <variant>
Expand Down
Loading
Loading