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
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ if(NOT MSVC)
APPEND ALL_EXAMPLES
task-sender
alloc-1
alloc-2
bulk
c++now-allocator
c++now-cancel
Expand Down
82 changes: 82 additions & 0 deletions examples/alloc-2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// examples/alloc-1.cpp -*-C++-*-
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

File header comment says examples/alloc-1.cpp but this is alloc-2.cpp, which can confuse readers and tooling that relies on the header banner. Update the comment to match the actual filename.

Suggested change
// examples/alloc-1.cpp -*-C++-*-
// examples/alloc-2.cpp -*-C++-*-

Copilot uses AI. Check for mistakes.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <beman/execution/task.hpp>
#include <beman/execution/execution.hpp>
#include <functional>
#include <iostream>
#include <memory>
#include <memory_resource>
#include <new>
#include <cstdlib>
#include <utility>

namespace ex = beman::execution;

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

struct tls_allocator : std::pmr::polymorphic_allocator<std::byte> {
thread_local static std::pmr::memory_resource* alloc;

tls_allocator() : std::pmr::polymorphic_allocator<std::byte>(alloc) {}

static void set(std::pmr::memory_resource* a) { alloc = a; }
};
thread_local std::pmr::memory_resource* tls_allocator::alloc{std::pmr::new_delete_resource()};

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

void* operator new(std::size_t n) {
auto p = std::malloc(n);
std::cout << " global new(" << n << ")->" << p << "\n";
Comment on lines +30 to +31
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The throwing global operator new(std::size_t) is required to throw std::bad_alloc on allocation failure; returning nullptr is undefined behavior for callers that assume a valid pointer. Consider checking the malloc result and throwing, or implement only the nothrow overload if you intend to return null.

Suggested change
auto p = std::malloc(n);
std::cout << " global new(" << n << ")->" << p << "\n";
void* p = std::malloc(n);
std::cout << " global new(" << n << ")->" << p << "\n";
if (!p) {
throw std::bad_alloc();
}

Copilot uses AI. Check for mistakes.
return p;
}
void operator delete(void* ptr) noexcept {
std::cout << " global operator delete(" << ptr << ")\n";
std::free(ptr);
}
void operator delete(void* ptr, std::size_t size) noexcept {
std::cout << " global operator delete(" << ptr << ", " << size << ")\n";
std::free(ptr);
}

struct resource : std::pmr::memory_resource {
void* do_allocate(std::size_t n, std::size_t) override {
auto p{std::malloc(n)};
std::cout << " resource::allocate(" << n << ")->" << p << "\n";
return p;
}
void do_deallocate(void* p, std::size_t n, std::size_t) override {
std::cout << " resource::deallocate(" << p << ", " << n << ")\n";
std::free(p);
Comment on lines +44 to +51
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

std::pmr::memory_resource::do_allocate(bytes, alignment) must honor the requested alignment. Ignoring the alignment parameter and using malloc can return misaligned storage for over-aligned allocations. Use an aligned allocation strategy (e.g., ::operator new(bytes, std::align_val_t(alignment)) + matching aligned delete, or std::aligned_alloc where available).

Suggested change
void* do_allocate(std::size_t n, std::size_t) override {
auto p{std::malloc(n)};
std::cout << " resource::allocate(" << n << ")->" << p << "\n";
return p;
}
void do_deallocate(void* p, std::size_t n, std::size_t) override {
std::cout << " resource::deallocate(" << p << ", " << n << ")\n";
std::free(p);
void* do_allocate(std::size_t n, std::size_t alignment) override {
void* p = nullptr;
#if defined(__cpp_aligned_new)
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
p = ::operator new(n, std::align_val_t(alignment));
} else
#endif
{
p = ::operator new(n);
}
std::cout << " resource::allocate(" << n << ")->" << p << "\n";
return p;
}
void do_deallocate(void* p, std::size_t n, std::size_t alignment) override {
std::cout << " resource::deallocate(" << p << ", " << n << ")\n";
#if defined(__cpp_aligned_new)
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
::operator delete(p, n, std::align_val_t(alignment));
} else
#endif
{
::operator delete(p, n);
}

Copilot uses AI. Check for mistakes.
}
bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; }
};

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

using allocator_type = tls_allocator;
struct alloc_env {
using allocator_type = ::allocator_type;
};
template <typename T = void>
using a_task = ex::task<T, alloc_env>;

a_task<int> async_fun(int value) { co_return value; }

int main(int ac, char*[]) {
resource res{};

std::cout << "not setting up an allocator:\n";
ex::sync_wait([ac]() -> a_task<> {
auto result{co_await async_fun(ac)};
std::cout << " result=" << result << "\n";
}());

std::cout << "setting up an allocator:\n";
tls_allocator::set(&res);
ex::sync_wait([ac]() -> a_task<> {
auto result{co_await async_fun(ac)};
std::cout << " result=" << result << "\n";
Comment on lines +77 to +80
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

tls_allocator::set(&res) stores a pointer to a stack resource in a thread_local that outlives res. Even though this example exits soon after, leaving a thread_local pointing at a soon-to-be-destroyed object is easy to copy/paste into real code and becomes a dangling pointer hazard. Consider resetting back to std::pmr::new_delete_resource() after the sync_wait, or wrap the change in an RAII guard object.

Copilot uses AI. Check for mistakes.
}());
}
Loading