This repository has been archived by the owner on Jan 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolves #205
- Loading branch information
Showing
4 changed files
with
259 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
#pragma once | ||
|
||
#include <array> | ||
#include <cstddef> | ||
#include <experimental/coroutine> | ||
#include <iterator> | ||
#include <memory> | ||
#include <optional> | ||
#include <span> | ||
#include <type_traits> | ||
|
||
namespace hal { | ||
|
||
/** | ||
* @brief An allocator aware coroutine for creating resumable functions | ||
* | ||
* When the coroutine finishes, meaning a `co_return` was called, the resumable | ||
* will continue to return the last yield value it had, thus calling a finished | ||
* resumable has well defined behavior. | ||
* | ||
* @tparam T - return type of the resumable function, cannot be void | ||
*/ | ||
template<typename T> | ||
struct resumable | ||
{ | ||
struct promise_type; | ||
using handle_t = std::experimental::coroutine_handle<promise_type>; | ||
|
||
struct promise_type | ||
{ | ||
resumable get_return_object() | ||
{ | ||
return resumable(handle_t::from_promise(*this)); | ||
} | ||
|
||
std::experimental::suspend_always initial_suspend() | ||
{ | ||
return {}; | ||
} | ||
|
||
std::experimental::suspend_always final_suspend() noexcept | ||
{ | ||
return {}; | ||
} | ||
|
||
void unhandled_exception() | ||
{ | ||
} | ||
|
||
template<std::convertible_to<T> From> | ||
std::experimental::suspend_always yield_value(From&& from) | ||
{ | ||
value = std::forward<From>(from); | ||
return {}; | ||
} | ||
|
||
void return_void() | ||
{ | ||
} | ||
|
||
template<class Allocator, typename... Args> | ||
void* operator new(size_t p_size, | ||
std::allocator_arg_t, | ||
Allocator* alloc, | ||
[[maybe_unused]] Args&... args) | ||
{ | ||
return alloc->allocate(p_size); | ||
} | ||
|
||
void operator delete(void*, size_t) | ||
{ | ||
} | ||
|
||
T value; | ||
}; | ||
|
||
resumable(handle_t p_handle) | ||
: m_coroutine_handle(p_handle) | ||
{ | ||
} | ||
|
||
~resumable() | ||
{ | ||
m_coroutine_handle.destroy(); | ||
} | ||
|
||
explicit operator bool() | ||
{ | ||
return !m_coroutine_handle.done(); | ||
} | ||
|
||
T operator()() | ||
{ | ||
if (!m_coroutine_handle.done()) { | ||
m_coroutine_handle(); | ||
} | ||
return std::move(m_coroutine_handle.promise().value); | ||
} | ||
|
||
private: | ||
handle_t m_coroutine_handle; | ||
}; | ||
} // namespace hal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#pragma once | ||
|
||
#include <array> | ||
#include <cstddef> | ||
#include <cstdint> | ||
#include <span> | ||
|
||
namespace hal { | ||
|
||
/** | ||
* @brief An Allocator that uses a pool of memory | ||
* | ||
* This allocator is monotonic and does not deallocate | ||
* | ||
* @tparam T - allocator type | ||
*/ | ||
template<class T> | ||
class allocator | ||
{ | ||
public: | ||
using value_type = T; | ||
|
||
/** | ||
* @brief Construct a new allocator object | ||
* | ||
* @param p_memory - pool of memory to use for the allocator. This memory must | ||
* outlive this object. | ||
*/ | ||
allocator(std::span p_memory) | ||
: m_memory(p_memory) | ||
{ | ||
} | ||
|
||
template<class U> | ||
constexpr allocator(const allocator<U, Size>&) noexcept | ||
{ | ||
} | ||
|
||
/** | ||
* @brief Allocate a number of elements from the memory pool | ||
* | ||
* @param p_number_of_elements - element count | ||
* @return T* - address of the allocated memory | ||
*/ | ||
[[nodiscard]] T* allocate(std::size_t p_number_of_elements) | ||
{ | ||
if (p_number_of_elements > m_memory.size()) { | ||
return nullptr; | ||
} | ||
|
||
T* result = m_memory.data(); | ||
m_memory = m_memory.subspan(p_number_of_elements); | ||
return result; | ||
} | ||
|
||
/** | ||
* @brief Deallocator that does nothing | ||
* | ||
* @param p_address | ||
* @param p_elements | ||
*/ | ||
void deallocate([[maybe_unused]] T* p_address, | ||
[[maybe_unused]] std::size_t p_elements) noexcept | ||
{ | ||
} | ||
|
||
template<class T, size_t TSize, class U, size_t USize> | ||
friend bool operator==(const allocator<T, TSize>&, const allocator<U, USize>&) | ||
{ | ||
return true; | ||
} | ||
|
||
template<class T, size_t TSize, class U, size_t USize> | ||
friend bool operator!=(const allocator<T, TSize>&, const allocator<U, USize>&) | ||
{ | ||
return false; | ||
} | ||
|
||
private: | ||
std::span<T> m_memory; | ||
}; | ||
|
||
/** | ||
* @brief Owning static memory allocator | ||
* | ||
* @tparam T - allocator type | ||
* @tparam Size - number of elements | ||
*/ | ||
template<class T, size_t Size> | ||
class static_allocator : public hal::allocator | ||
{ | ||
public: | ||
/** | ||
* @brief Construct a new static allocator object | ||
* | ||
*/ | ||
static_allocator() | ||
: allocator(m_buffer) | ||
{ | ||
} | ||
|
||
private: | ||
std::array<T, Size> m_buffer; | ||
}; | ||
} // namespace hal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#include <libhal/resumable.hpp> | ||
|
||
#include <boost/ut.hpp> | ||
|
||
#include <array> | ||
|
||
namespace hal { | ||
boost::ut::suite resumable_test = []() { | ||
using namespace boost::ut; | ||
|
||
"hal::resumable<T>()"_test = []() { | ||
"hal::resumable<int>()"_test = []() { | ||
// Setup | ||
auto resumable_lambda = []() -> hal::resumable<int> { | ||
co_yield 1; | ||
co_yield 2; | ||
co_return; | ||
}; | ||
|
||
// Exercise | ||
auto resumer = resumable_lambda(); | ||
auto resumer_state1 = bool{ resumer }; | ||
auto value1 = resumer(); | ||
auto value2 = resumer(); | ||
auto resumer_state2 = bool{ resumer }; | ||
auto value3 = resumer(); | ||
auto resumer_state3 = bool{ resumer }; | ||
auto value4 = resumer(); | ||
auto resumer_state4 = bool{ resumer }; | ||
|
||
// Verify | ||
expect(that % resumer_state1); | ||
expect(that % 1 == value1); | ||
expect(that % 2 == value2); | ||
expect(that % resumer_state2); | ||
expect(that % 2 == value3); | ||
expect(that % 2 == value4); | ||
expect(that % !resumer_state3); | ||
expect(that % !resumer_state4); | ||
}; | ||
}; | ||
}; | ||
} |