Skip to content

Commit 61fb73d

Browse files
authored
exec::function cleanup (#2089)
* Split get_frame_allocator out of function.hpp I'm about to write a new algorithm that injects a frame allocator into its child's environment, so I'm going to need to be able to refer to `get_frame_allocator_t` but I don't need to refer to `function` so I'm splitting the declarations. * Allow get_frame_allocator to return both allocators and memory_resources Also, add an allocator named `__frame_allocator` that's basically `polymorphic_allocator` but knows the concrete type of the `memory_resource*` it uses for allocations. * Wire __frame_allocator_t into function Use the new `exec::__fa::__frame_allocator_t` alias template in `exec::function`'s allocator-selection logic, in anticipation of supporting a wider variety of allocators. * Guarantee that function advertises a frame allocator to children With this change, you don't need to add `some_allocator(get_frame_allocator_t)` to the list of type-erased queries for the erased operation to see that its receiver's environment contains a frame allocator. If the list of type-erased queries *does* contain that type, it's preferred, allowing the user to override the return type of `get_frame_allocator` but we otherwise provide a `std::pmr::polymorphic_allocator<std::byte>` that is backed by whichever allocator was actually used to allocate the operation state.
1 parent 02d671d commit 61fb73d

9 files changed

Lines changed: 1094 additions & 148 deletions

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ if (STDEXEC_BUILD_TESTS)
129129
BUILD_EXPORT_SET stdexec-exports
130130
CPM_ARGS
131131
URL https://github.com/catchorg/Catch2/archive/refs/tags/v${Catch2_VERSION}.zip
132-
OPTIONS "CMAKE_CXX_STANDARD 20"
132+
OPTIONS
133+
"CMAKE_CXX_STANDARD 20"
134+
"CATCH_CONFIG_NO_POSIX_SIGNALS ON"
133135
)
134136
endif()
135137

include/exec/__frame_allocator.hpp

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/* Copyright (c) 2026 Ian Petersen
2+
* Copyright (c) 2026 NVIDIA Corporation
3+
*
4+
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
5+
* (the "License"); you may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* https://llvm.org/LICENSE.txt
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#pragma once
17+
18+
#include "../stdexec/__detail/__concepts.hpp"
19+
20+
#include <cstddef>
21+
#include <memory>
22+
#include <memory_resource>
23+
#include <type_traits>
24+
25+
#include "../stdexec/__detail/__prologue.hpp"
26+
27+
//! Defines template <class _Delegate> exec::__frame_allocator_t
28+
//!
29+
//! The intended use for __frame_allocator_t is the dynamic allocation of operation
30+
//! states and/or coroutine frames. __frame_allocator_t models the Allocator named
31+
//! requirement in terms of its type parameter, _Delegate.
32+
//!
33+
//! _Delegate may be one of:
34+
//! - Another allocator, in which case __frame_allocator_t<_Delegate> is just _Delegate
35+
//! rebound to std::byte;
36+
//! - std::pmr::memory_resource*, in which case __frame_allocator_t<_Delegate> is just
37+
//! std::pmr::polymorphic_allocator<std::byte>; or
38+
//! - T* for some T that inherits from std::pmr::memor_resource, in which case
39+
//! __frame_allocator_t is an allocator that behaves like
40+
//! std::pmr::polymorphic_allocator<std::byte>, but knows the concrete type of its
41+
//! memory_resource and so therefore may be able to avoid virtual dispatch.
42+
//!
43+
//! Given that __frame_allocator_t<_Delegate> is just an alias to _Delegate when _Delegate
44+
//! is an allocator type, it's up to that type whether its construct member function does
45+
//! "uses-allocator construction". When _Delegate is a pointer to a memory_resource,
46+
//! __frame_allocator_t<_Delegate>::construct does "uses-allocator construction".
47+
namespace experimental::execution
48+
{
49+
namespace __fa
50+
{
51+
using namespace STDEXEC;
52+
53+
template <class _Delegate>
54+
struct __frame_allocator;
55+
56+
//! Handle the case that _Delegate is an allocator already
57+
//!
58+
//! In this case, __frame_allocator_t<Delegate> is just an alias to _Delegate but
59+
//! rebound to std::byte.
60+
template <class _Delegate>
61+
requires __simple_allocator<_Delegate>
62+
struct __frame_allocator<_Delegate>
63+
{
64+
template <class _Ty>
65+
using type = std::allocator_traits<_Delegate>::template rebind_alloc<_Ty>;
66+
};
67+
68+
//! Handle the case that _Delegate is exactly std::pmr::memory_resource *
69+
//!
70+
//! In this case, __frame_allocator_t<_Delegate> is exactly
71+
//! std::pmr::polymorphic_allocator<std::byte>.
72+
template <>
73+
struct __frame_allocator<std::pmr::memory_resource *>
74+
{
75+
template <class _Ty>
76+
using type = std::pmr::polymorphic_allocator<_Ty>;
77+
};
78+
79+
//! Handle the case that _Delegate is a pointer to a type that derives from
80+
//! std::pmr::memory_resource
81+
//!
82+
//! In this case, __frame_allocator_t<_Delegate> is an allocator that behaves like
83+
//! std::pmr::polymorphic_allocator<std::byte> except that it knows the concrete type
84+
//! of its memory resource. In other words, allocation and deallocation are delegated
85+
//! to the given resource, construct does uses-allocator construction, etc.
86+
template <class _Delegate>
87+
requires __std::derived_from<_Delegate, std::pmr::memory_resource>
88+
struct __frame_allocator<_Delegate *>
89+
{
90+
template <class _Ty>
91+
struct type
92+
{
93+
using value_type = _Ty;
94+
using pointer = value_type *;
95+
96+
// polymorphic_allocator's default constructor grabs the default memory resource,
97+
// which we can't do because there's no default for _Delegate
98+
99+
/*implicit*/ constexpr type(_Delegate *__resource) noexcept
100+
: __resource_(__resource)
101+
{}
102+
103+
constexpr type(type const &) noexcept = default;
104+
105+
template <class _Uy>
106+
/*implicit*/ constexpr type(type<_Uy> const &other) noexcept
107+
: __resource_(other.resource())
108+
{}
109+
110+
constexpr ~type() = default;
111+
112+
constexpr type &operator=(type const &) noexcept = default;
113+
114+
constexpr pointer allocate(std::size_t __n)
115+
{
116+
return static_cast<pointer>(
117+
__resource_->allocate(__n * sizeof(value_type), alignof(value_type)));
118+
}
119+
120+
constexpr void deallocate(void *__p, std::size_t __n) noexcept
121+
{
122+
__resource_->deallocate(__p, __n * sizeof(value_type), alignof(value_type));
123+
}
124+
125+
template <class _Uy, class... _Args>
126+
constexpr void construct(_Uy *__p, _Args &&...__args)
127+
{
128+
(void) std::uninitialized_construct_using_allocator(__p,
129+
*this,
130+
static_cast<_Args &&>(__args)...);
131+
}
132+
133+
template <class _Uy>
134+
constexpr void destroy(_Uy *__p) noexcept
135+
{
136+
__p->~_Uy();
137+
}
138+
139+
constexpr _Delegate *resource() const noexcept
140+
{
141+
return __resource_;
142+
}
143+
144+
private:
145+
_Delegate *__resource_;
146+
};
147+
};
148+
} // namespace __fa
149+
150+
template <class _Delegate>
151+
using __frame_allocator_t =
152+
__fa::__frame_allocator<std::remove_cvref_t<_Delegate>>::template type<std::byte>;
153+
} // namespace experimental::execution
154+
155+
namespace exec = experimental::execution;
156+
157+
#include "../stdexec/__detail/__epilogue.hpp"

0 commit comments

Comments
 (0)