Skip to content

Commit

Permalink
Merge pull request #6 from bemanproject/add-allocator-tool-tests
Browse files Browse the repository at this point in the history
Add allocator tool tests
  • Loading branch information
dietmarkuehl authored Jan 23, 2025
2 parents eff5974 + d9a2e56 commit dab106d
Show file tree
Hide file tree
Showing 15 changed files with 388 additions and 113 deletions.
20 changes: 13 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
#-dk: note to self: PATH=/opt/llvm-19.1.6/bin:$PATH LDFLAGS=-fuse-ld=lld

.PHONY: config test default compile clean

BUILDDIR = build
PRESET = gcc-debug
UNAME = $(shell uname -s)
ifeq ($(UNAME),Darwin)
PRESET = appleclang-debug
endif

default: compile

compile: config
#cmake --build $(BUILDDIR) -j
cmake --workflow --preset=appleclang-debug
compile:
cmake --workflow --preset=$(PRESET)

format:
git clang-format main
list:
cmake --workflow --list-presets

config:
#CXXFLAGS=-fsanitize=thread cmake -DCMAKE_BUILD_TYPE=Debug -B $(BUILDDIR)
format:
git clang-format

test: compile
cd $(BUILDDIR); ctest
Expand Down
4 changes: 4 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ foreach(example ${ALL_EXAMPLES})
add_executable(beman.lazy.examples.${example})
target_sources(beman.lazy.examples.${example} PRIVATE ${example}.cpp)
target_link_libraries(beman.lazy.examples.${example} beman::lazy)
add_test(
NAME beman.lazy.examples.${example}
COMMAND $<TARGET_FILE:beman.lazy.examples.${example}>
)
endforeach()
60 changes: 41 additions & 19 deletions examples/alloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ struct alloc_aware {
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
};

struct test_base {
template <typename... Args>
void* operator new(std::size_t size, Args&&...) {
void* ptr{::operator new(size)};
std::cout << "custom alloc(" << size << ") -> " << ptr << "\n";
return ptr;
}
void operator delete(void* ptr, std::size_t size) {
std::cout << "custom dealloc(" << ptr << ", " << size << ")\n";
::operator delete(ptr, size);
}
};
struct test : test_base {};

int main() {
std::cout << "running just\n";
ex::sync_wait(ex::just(0));
Expand All @@ -69,25 +83,33 @@ int main() {
std::cout << "ex::lazy<void, alloc_aware> done\n\n";

fixed_resource<2048> resource;
std::cout << "running ex::lazy<void, alloc_aware> with alloc\n";
ex::sync_wait([](auto&&...) -> ex::lazy<void, alloc_aware> {
co_await ex::just(0);
co_await ex::just(0);
}(std::allocator_arg, &resource));
std::cout << "ex::lazy<void, alloc_aware> with alloc done\n\n";
if constexpr (true) {
std::cout << "running ex::lazy<void, alloc_aware> with alloc\n";
ex::sync_wait([](auto&&...) -> ex::lazy<void, alloc_aware> {
co_await ex::just(0);
co_await ex::just(0);
}(std::allocator_arg, &resource));
std::cout << "ex::lazy<void, alloc_aware> with alloc done\n\n";
}

std::cout << "running ex::lazy<void> with alloc\n";
ex::sync_wait([](auto&&...) -> ex::lazy<void> {
co_await ex::just(0);
co_await ex::just(0);
}(std::allocator_arg, &resource));
std::cout << "ex::lazy<void> with alloc done\n\n";
if constexpr (false) {
std::cout << "running ex::lazy<void> with alloc\n";
ex::sync_wait([](auto&&...) -> ex::lazy<void> {
co_await ex::just(0);
co_await ex::just(0);
}(std::allocator_arg, &resource));
std::cout << "ex::lazy<void> with alloc done\n\n";
}

if constexpr (false) {
std::cout << "running ex::lazy<void, alloc_aware> extracting alloc\n";
ex::sync_wait([](auto&&, [[maybe_unused]] auto* res) -> ex::lazy<void, alloc_aware> {
auto alloc = co_await ex::read_env(ex::get_allocator);
static_assert(std::same_as<std::pmr::polymorphic_allocator<std::byte>, decltype(alloc)>);
assert(alloc == std::pmr::polymorphic_allocator<std::byte>(res));
}(std::allocator_arg, &resource));
std::cout << "ex::lazy<void, alloc_aware> extracting alloc done\n\n";
}

std::cout << "running ex::lazy<void, alloc_aware> extracting alloc\n";
ex::sync_wait([](auto&&, [[maybe_unused]] auto* res) -> ex::lazy<void, alloc_aware> {
auto alloc = co_await ex::read_env(ex::get_allocator);
static_assert(std::same_as<std::pmr::polymorphic_allocator<std::byte>, decltype(alloc)>);
assert(alloc == std::pmr::polymorphic_allocator<std::byte>(res));
}(std::allocator_arg, &resource));
std::cout << "ex::lazy<void, alloc_aware> extracting alloc done\n\n";
delete new test{};
}
23 changes: 14 additions & 9 deletions examples/async-lock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <exception>
#include <queue>
#include <mutex>
#include <string>
#include <condition_variable>
#include <beman/execution26/execution.hpp>
#include <beman/lazy/lazy.hpp>
Expand Down Expand Up @@ -67,7 +68,7 @@ struct request {

void stop(queue& q) { ex::sync_wait(request{0, q}); }

int main() {
int main(int ac, char* av[]) {
queue q{};

std::thread process([&q] {
Expand All @@ -80,14 +81,18 @@ int main() {
}
});

ex::sync_wait(ex::detail::write_env(
[](queue& que) -> ex::lazy<void> {
std::cout << std::this_thread::get_id() << " start\n" << std::flush;
auto result = co_await request{17, que};
std::cout << std::this_thread::get_id() << " result=" << result << "\n" << std::flush;
stop(que);
}(q),
ex::detail::make_env(ex::get_scheduler, ex::inline_scheduler{})));
auto work{[](queue& que) -> ex::lazy<void> {
std::cout << std::this_thread::get_id() << " start\n" << std::flush;
auto result = co_await request{17, que};
std::cout << std::this_thread::get_id() << " result=" << result << "\n" << std::flush;
stop(que);
}};

if (1 < ac && av[1] == std::string_view("run-it")) {
ex::sync_wait(ex::detail::write_env(work(q), ex::detail::make_env(ex::get_scheduler, ex::inline_scheduler{})));
} else {
ex::sync_wait(work(q));
}
process.join();
std::cout << "done\n";
}
10 changes: 6 additions & 4 deletions examples/loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <iostream>
#include <string>
#include <beman/execution26/execution.hpp>
#include <beman/lazy/lazy.hpp>

Expand All @@ -12,13 +13,14 @@ ex::lazy<void> loop() {
co_await ex::just(i);
}

int main() {
int main(int ac, char* av[]) {
auto count = ac < 1 && av[1] == std::string_view("run-it") ? 1000000 : 1000;
ex::sync_wait(
// ex::detail::write_env(
[]() -> ex::lazy<void> {
for (int i{}; i < 1000000; ++i)
[](auto count) -> ex::lazy<void> {
for (int i{}; i < count; ++i)
co_await ex::just(i);
}()
}(count)
//, ex::detail::make_env(ex::get_scheduler, ex::inline_scheduler{}))
);
}
64 changes: 0 additions & 64 deletions include/beman/lazy/detail/allocator.hpp

This file was deleted.

38 changes: 38 additions & 0 deletions include/beman/lazy/detail/allocator_of.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// include/beman/lazy/detail/allocator_of.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_LAZY_DETAIL_ALLOCATOR_OF
#define INCLUDED_INCLUDE_BEMAN_LAZY_DETAIL_ALLOCATOR_OF

#include <concepts>
#include <memory>

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

namespace beman::lazy::detail {
/*!
* \brief Utility to get an allocator type from a context
* \headerfile beman/lazy/lazy.hpp <beman/lazy/lazy.hpp>
* \internal
*/
template <typename>
struct allocator_of {
using type = std::allocator<std::byte>;
};
template <typename Context>
requires requires { typename Context::allocator_type; }
struct allocator_of<Context> {
using type = typename Context::allocator_type;
static_assert(
requires(type& a, std::size_t s, std::byte* ptr) {
{ a.allocate(s) } -> std::same_as<std::byte*>;
a.deallocate(ptr, s);
}, "The allocator_type needs to be an allocator of std::byte");
};
template <typename Context>
using allocator_of_t = typename allocator_of<Context>::type;
} // namespace beman::lazy::detail

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

#endif
82 changes: 82 additions & 0 deletions include/beman/lazy/detail/allocator_support.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// include/beman/lazy/detail/allocator_support.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_LAZY_DETAIL_ALLOCATOR_SUPPORT
#define INCLUDED_BEMAN_LAZY_DETAIL_ALLOCATOR_SUPPORT

#include <beman/lazy/detail/find_allocator.hpp>
#include <array>
#include <concepts>
#include <cstddef>
#include <memory>
#include <new>

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

namespace beman::lazy::detail {
/*!
* \brief Utility adding allocator support to type by embedding the allocator
* \headerfile beman/lazy/lazy.hpp <beman/lazy/lazy.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 somehow determine an allocator based on the arguments
* passed to the coroutine. Even worse, the operator delete only gets passed
* a pointer to delete and a size. To determine the correct allocator the
* operator delete needs to located it based on this information. Putting
* the allocator after actually used memory causes the address sanitizer to
* object! So, the current strategy is to embed space for the allocator
* into the object and pull it out from there.
*/
template <typename Allocator>
struct allocator_support {
using allocator_traits = std::allocator_traits<Allocator>;

static std::size_t offset(std::size_t size) {
return (size + alignof(Allocator) - 1u) & ~(alignof(Allocator) - 1u);
}
static Allocator* get_allocator(void* ptr, std::size_t size) {
ptr = static_cast<std::byte*>(ptr) + offset(size);
return ::std::launder(reinterpret_cast<Allocator*>(ptr));
}

template <typename... A>
static void* operator new(std::size_t size, A&&... a) {
if constexpr (::std::same_as<Allocator, ::std::allocator<::std::byte>>) {
Allocator alloc{};
return allocator_traits::allocate(alloc, size);

} else {
Allocator alloc{::beman::lazy::detail::find_allocator<Allocator>(a...)};
void* ptr{allocator_traits::allocate(alloc, allocator_support::offset(size) + sizeof(Allocator))};
new (allocator_support::get_allocator(ptr, size)) Allocator(alloc);
return ptr;
}
}
template <typename... A>
static void operator delete(void* ptr, std::size_t size, A&&...) {
allocator_support::operator delete(ptr, size);
}
static void operator delete(void* ptr, std::size_t size) {
if constexpr (::std::same_as<Allocator, ::std::allocator<::std::byte>>) {
Allocator alloc{};
allocator_traits::deallocate(alloc, static_cast<std::byte*>(ptr), size);
} else {
Allocator* aptr{allocator_support::get_allocator(ptr, size)};
Allocator alloc{*aptr};
aptr->~Allocator();
allocator_traits::deallocate(
alloc, static_cast<std::byte*>(ptr), allocator_support::offset(size) + sizeof(Allocator));
}
}
};
} // namespace beman::lazy::detail

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

#endif
Loading

0 comments on commit dab106d

Please sign in to comment.