Loading...
Searching...
No Matches
Awaitable.h
Go to the documentation of this file.
#ifndef SDBUS_CXX_AWAITABLE_H_
#define SDBUS_CXX_AWAITABLE_H_
#include <atomic>
#include <cassert>
#if __has_include(<coroutine>)
#include <coroutine>
#endif
#include <cstdint>
#include <exception>
#include <memory>
#include <type_traits>
#include <variant>
namespace sdbus {
// Forward declarations
class AsyncMethodInvoker;
namespace internal {
class Proxy;
} // namespace internal
/********************************************/
enum class AwaitableState : uint8_t
{
NotReady, // Initial state: callback hasn't fired yet
Waiting, // Coroutine is suspended and waiting for callback
Completed // Callback completed, result is ready
};
// Shared data
template <typename T>
struct AwaitableData
{
using result_type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
std::variant<result_type, std::exception_ptr> result;
std::atomic<AwaitableState> status{AwaitableState::NotReady};
#ifdef __cpp_lib_coroutine
// Keep the handle as the last member to mainting ABI compatibility
// with clients without coroutine support.
std::coroutine_handle<> handle;
#endif // __cpp_lib_coroutine
void resumeCoroutine()
{
#ifdef __cpp_lib_coroutine
handle.resume();
#endif // __cpp_lib_coroutine
}
};
/********************************************/
template <typename T>
class Awaitable
{
#ifdef __cpp_lib_coroutine
public:
// Called when the coroutine is co_await'ed. Returns true if the coroutine should be suspended.
[[nodiscard]] bool await_ready() const noexcept
{
return data_->status.load(std::memory_order_acquire) == AwaitableState::Completed;
}
// Called when the coroutine is suspended, returning false here will immediately
// resume the coroutine.
bool await_suspend(std::coroutine_handle<> handle) noexcept
{
data_->handle = handle;
// Attempt transition from NotReady to Waiting.
AwaitableState expected = AwaitableState::NotReady;
return data_->status.compare_exchange_strong(expected, AwaitableState::Waiting, std::memory_order_acq_rel);
}
// Called when the coroutine is resumed. Returns the result or throws the exception.
[[nodiscard]] T await_resume() const
{
if (auto* exception = std::get_if<std::exception_ptr>(&data_->result); exception != nullptr)
std::rethrow_exception(*exception);
if constexpr (std::is_void_v<T>)
return;
else
return std::get<T>(std::move(data_->result));
}
#endif // __cpp_lib_coroutine
private:
friend internal::Proxy;
friend AsyncMethodInvoker;
explicit Awaitable(std::shared_ptr<AwaitableData<T>> data)
: data_(std::move(data))
{
assert(data_ != nullptr);
}
std::shared_ptr<AwaitableData<T>> data_;
};
} // namespace sdbus
#endif // SDBUS_CXX_AWAITABLE_H_
sdbus::AwaitableState
AwaitableState
Definition Awaitable.h:60
sdbus::AsyncMethodInvoker
Definition ConvenienceApiClasses.h:128
sdbus::AwaitableData
Definition Awaitable.h:69