common/fiber: Move all member variables into impl class

Hides all of the implementation details for users of the class. This has
the benefit of reducing includes and also making the fiber classes
movable again.
This commit is contained in:
Lioncash 2020-11-06 20:29:54 -05:00
parent 91a45834fd
commit 00fb79b2f3
2 changed files with 86 additions and 89 deletions

View File

@ -4,6 +4,8 @@
#include "common/assert.h"
#include "common/fiber.h"
#include "common/spin_lock.h"
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
#else
@ -14,18 +16,45 @@ namespace Common {
constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
#if defined(_WIN32) || defined(WIN32)
struct Fiber::FiberImpl {
SpinLock guard{};
std::function<void(void*)> entry_point;
std::function<void(void*)> rewind_point;
void* rewind_parameter{};
void* start_parameter{};
std::shared_ptr<Fiber> previous_fiber;
bool is_thread_fiber{};
bool released{};
#if defined(_WIN32) || defined(WIN32)
LPVOID handle = nullptr;
LPVOID rewind_handle = nullptr;
#else
alignas(64) std::array<u8, default_stack_size> stack;
alignas(64) std::array<u8, default_stack_size> rewind_stack;
u8* stack_limit;
u8* rewind_stack_limit;
boost::context::detail::fcontext_t context;
boost::context::detail::fcontext_t rewind_context;
#endif
};
void Fiber::SetStartParameter(void* new_parameter) {
impl->start_parameter = new_parameter;
}
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
impl->rewind_point = std::move(rewind_func);
impl->rewind_parameter = rewind_param;
}
#if defined(_WIN32) || defined(WIN32)
void Fiber::Start() {
ASSERT(previous_fiber != nullptr);
previous_fiber->guard.unlock();
previous_fiber.reset();
entry_point(start_parameter);
ASSERT(impl->previous_fiber != nullptr);
impl->previous_fiber->impl->guard.unlock();
impl->previous_fiber.reset();
impl->entry_point(impl->start_parameter);
UNREACHABLE();
}
@ -34,58 +63,54 @@ void Fiber::OnRewind() {
DeleteFiber(impl->handle);
impl->handle = impl->rewind_handle;
impl->rewind_handle = nullptr;
rewind_point(rewind_parameter);
impl->rewind_point(impl->rewind_parameter);
UNREACHABLE();
}
void Fiber::FiberStartFunc(void* fiber_parameter) {
auto fiber = static_cast<Fiber*>(fiber_parameter);
auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->Start();
}
void Fiber::RewindStartFunc(void* fiber_parameter) {
auto fiber = static_cast<Fiber*>(fiber_parameter);
auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->OnRewind();
}
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
impl = std::make_unique<FiberImpl>();
: impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func);
impl->start_parameter = start_parameter;
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (released) {
if (impl->released) {
return;
}
// Make sure the Fiber is not being used
const bool locked = guard.try_lock();
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
guard.unlock();
impl->guard.unlock();
}
DeleteFiber(impl->handle);
}
void Fiber::Exit() {
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
if (!is_thread_fiber) {
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
if (!impl->is_thread_fiber) {
return;
}
ConvertFiberToThread();
guard.unlock();
released = true;
}
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
rewind_point = std::move(rewind_func);
rewind_parameter = rewind_param;
impl->guard.unlock();
impl->released = true;
}
void Fiber::Rewind() {
ASSERT(rewind_point);
ASSERT(impl->rewind_point);
ASSERT(impl->rewind_handle == nullptr);
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
SwitchToFiber(impl->rewind_handle);
@ -94,39 +119,30 @@ void Fiber::Rewind() {
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->guard.lock();
to->previous_fiber = from;
to->impl->guard.lock();
to->impl->previous_fiber = from;
SwitchToFiber(to->impl->handle);
ASSERT(from->previous_fiber != nullptr);
from->previous_fiber->guard.unlock();
from->previous_fiber.reset();
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
fiber->guard.lock();
fiber->impl->guard.lock();
fiber->impl->handle = ConvertThreadToFiber(nullptr);
fiber->is_thread_fiber = true;
fiber->impl->is_thread_fiber = true;
return fiber;
}
#else
struct Fiber::FiberImpl {
alignas(64) std::array<u8, default_stack_size> stack;
alignas(64) std::array<u8, default_stack_size> rewind_stack;
u8* stack_limit;
u8* rewind_stack_limit;
boost::context::detail::fcontext_t context;
boost::context::detail::fcontext_t rewind_context;
};
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
ASSERT(previous_fiber != nullptr);
previous_fiber->impl->context = transfer.fctx;
previous_fiber->guard.unlock();
previous_fiber.reset();
entry_point(start_parameter);
ASSERT(impl->previous_fiber != nullptr);
impl->previous_fiber->impl->context = transfer.fctx;
impl->previous_fiber->impl->guard.unlock();
impl->previous_fiber.reset();
impl->entry_point(impl->start_parameter);
UNREACHABLE();
}
@ -137,23 +153,24 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
u8* tmp = impl->stack_limit;
impl->stack_limit = impl->rewind_stack_limit;
impl->rewind_stack_limit = tmp;
rewind_point(rewind_parameter);
impl->rewind_point(impl->rewind_parameter);
UNREACHABLE();
}
void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
auto fiber = static_cast<Fiber*>(transfer.data);
auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->Start(transfer);
}
void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
auto fiber = static_cast<Fiber*>(transfer.data);
auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->OnRewind(transfer);
}
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
impl = std::make_unique<FiberImpl>();
: impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func);
impl->start_parameter = start_parameter;
impl->stack_limit = impl->stack.data();
impl->rewind_stack_limit = impl->rewind_stack.data();
u8* stack_base = impl->stack_limit + default_stack_size;
@ -161,37 +178,31 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete
boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
}
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
rewind_point = std::move(rewind_func);
rewind_parameter = rewind_param;
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (released) {
if (impl->released) {
return;
}
// Make sure the Fiber is not being used
const bool locked = guard.try_lock();
const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
guard.unlock();
impl->guard.unlock();
}
}
void Fiber::Exit() {
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
if (!is_thread_fiber) {
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
if (!impl->is_thread_fiber) {
return;
}
guard.unlock();
released = true;
impl->guard.unlock();
impl->released = true;
}
void Fiber::Rewind() {
ASSERT(rewind_point);
ASSERT(impl->rewind_point);
ASSERT(impl->rewind_context == nullptr);
u8* stack_base = impl->rewind_stack_limit + default_stack_size;
impl->rewind_context =
@ -202,19 +213,19 @@ void Fiber::Rewind() {
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->guard.lock();
to->previous_fiber = from;
to->impl->guard.lock();
to->impl->previous_fiber = from;
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
ASSERT(from->previous_fiber != nullptr);
from->previous_fiber->impl->context = transfer.fctx;
from->previous_fiber->guard.unlock();
from->previous_fiber.reset();
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
fiber->guard.lock();
fiber->is_thread_fiber = true;
fiber->impl->guard.lock();
fiber->impl->is_thread_fiber = true;
return fiber;
}

View File

@ -7,9 +7,6 @@
#include <functional>
#include <memory>
#include "common/common_types.h"
#include "common/spin_lock.h"
#if !defined(_WIN32) && !defined(WIN32)
namespace boost::context::detail {
struct transfer_t;
@ -41,8 +38,8 @@ public:
Fiber(const Fiber&) = delete;
Fiber& operator=(const Fiber&) = delete;
Fiber(Fiber&&) = delete;
Fiber& operator=(Fiber&&) = delete;
Fiber(Fiber&&) = default;
Fiber& operator=(Fiber&&) = default;
/// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber.
@ -57,9 +54,7 @@ public:
void Exit();
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
void SetStartParameter(void* new_parameter) {
start_parameter = new_parameter;
}
void SetStartParameter(void* new_parameter);
private:
Fiber();
@ -77,16 +72,7 @@ private:
#endif
struct FiberImpl;
SpinLock guard{};
std::function<void(void*)> entry_point;
std::function<void(void*)> rewind_point;
void* rewind_parameter{};
void* start_parameter{};
std::shared_ptr<Fiber> previous_fiber;
std::unique_ptr<FiberImpl> impl;
bool is_thread_fiber{};
bool released{};
};
} // namespace Common