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
111 changes: 65 additions & 46 deletions modules/async_context.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,71 @@ public:
using handle_type = std::coroutine_handle<>;
using full_handle_type = std::coroutine_handle<promise_type>;

future(future const& p_other) = delete;
future& operator=(future const& p_other) = delete;

/**
* @brief Default initialization for a void future
*
* This future will considered to be done on creation.
*/
future()
requires(std::is_void_v<T>)
: m_state(std::monostate{})
{
}

/**
* @brief Construct a future with a value
*
* This future will considered to be done and will contain just the value
* passed into this.
*
* @tparam U - type that can construct a type T (which includes T itself)
*/
template<typename U>
constexpr future(U&& p_value) noexcept
requires std::is_constructible_v<T, U&&>
{
m_state.template emplace<T>(std::forward<U>(p_value));
};

constexpr future(future&& p_other) noexcept
: m_state(std::exchange(p_other.m_state, std::monostate{}))
{
if (std::holds_alternative<handle_type>(m_state)) {
auto handle = std::get<handle_type>(m_state);
full_handle_type::from_address(handle.address())
.promise()
.set_object_address(&m_state);
}
}

constexpr future& operator=(future&& p_other) noexcept
{
if (this != &p_other) {
m_state = std::exchange(p_other.m_state, std::monostate{});
if (std::holds_alternative<handle_type>(m_state)) {
auto handle = std::get<handle_type>(m_state);
full_handle_type::from_address(handle.address())
.promise()
.set_object_address(&m_state);
}
}
return *this;
}

constexpr ~future()
{
if (std::holds_alternative<handle_type>(m_state)) {
auto handle = std::get<handle_type>(m_state);
full_handle_type::from_address(handle.address())
.promise()
.get_context()
.cancel();
}
}

constexpr void resume() const
{
if (std::holds_alternative<handle_type>(m_state)) {
Expand Down Expand Up @@ -1023,52 +1088,6 @@ public:
return awaiter{ *this };
}

template<typename U>
constexpr future(U&& p_value) noexcept
requires std::is_constructible_v<T, U&&>
{
m_state.template emplace<T>(std::forward<U>(p_value));
};

future(future const& p_other) = delete;
future& operator=(future const& p_other) = delete;

constexpr future(future&& p_other) noexcept
: m_state(std::exchange(p_other.m_state, std::monostate{}))
{
if (std::holds_alternative<handle_type>(m_state)) {
auto handle = std::get<handle_type>(m_state);
full_handle_type::from_address(handle.address())
.promise()
.set_object_address(&m_state);
}
}

constexpr future& operator=(future&& p_other) noexcept
{
if (this != &p_other) {
m_state = std::exchange(p_other.m_state, std::monostate{});
if (std::holds_alternative<handle_type>(m_state)) {
auto handle = std::get<handle_type>(m_state);
full_handle_type::from_address(handle.address())
.promise()
.set_object_address(&m_state);
}
}
return *this;
}

constexpr ~future()
{
if (std::holds_alternative<handle_type>(m_state)) {
auto handle = std::get<handle_type>(m_state);
full_handle_type::from_address(handle.address())
.promise()
.get_context()
.cancel();
}
}

private:
friend promise_type;

Expand Down
48 changes: 47 additions & 1 deletion tests/basics.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,28 @@ import test_utils;
boost::ut::suite<"basics"> basics = []() {
using namespace boost::ut;

"sync return"_test = []() {
"sync return type void"_test = []() {
// Setup
test_context ctx;

unsigned step = 0;
auto sync_coroutine = [&step](async::context&) -> async::future<void> {
step = 1;
return {};
};

// Exercise
auto future = sync_coroutine(ctx);

// Verify
expect(that % 0 == ctx.memory_used());
expect(that % not ctx.info->scheduled_called_once);
expect(that % future.done());
expect(that % future.has_value());
expect(that % 1 == step);
};

"sync return type int"_test = []() {
// Setup
test_context ctx;

Expand All @@ -32,6 +53,31 @@ boost::ut::suite<"basics"> basics = []() {
expect(that % 1 == step);
};

"sync return type std::string"_test = []() {
// Setup
test_context ctx;

static constexpr auto expected_return_value = "Hello, World\n";

unsigned step = 0;
auto sync_coroutine =
[&step](async::context&) -> async::future<std::string> {
step = 1;
return expected_return_value;
};

// Exercise
auto future = sync_coroutine(ctx);

// Verify
expect(that % 0 == ctx.memory_used());
expect(that % not ctx.info->scheduled_called_once);
expect(that % future.done());
expect(that % future.has_value());
expect(that % expected_return_value == future.value());
expect(that % 1 == step);
};

"co_return"_test = []() {
// Setup
test_context ctx;
Expand Down