feat/support_fiber #2
@ -12,59 +12,42 @@ namespace tile {
|
|||||||
namespace fiber {
|
namespace fiber {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
#define SET_PREV_FCTX(new_fctx) \
|
|
||||||
do { \
|
|
||||||
if (PrevFiber()) { \
|
|
||||||
PrevFiber()->ctx_->fctx = (new_fctx); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
static thread_local Fiber *tls_current_fiber = nullptr;
|
static thread_local Fiber *tls_current_fiber = nullptr;
|
||||||
static thread_local Fiber *tls_master_fiber = nullptr;
|
static thread_local Fiber *tls_master_fiber = nullptr;
|
||||||
static thread_local Fiber *tls_prev_fiber = nullptr;
|
|
||||||
|
|
||||||
constexpr std::size_t kStackSize = 128 * 1024; // 128k
|
constexpr std::size_t kStackSize = 128 * 1024; // 128k
|
||||||
constexpr std::size_t kAlignSize = 16;
|
constexpr std::size_t kAlignSize = 16;
|
||||||
|
|
||||||
struct alignas(hardware_destructive_interference_size) Fiber::FiberContext {
|
struct alignas(hardware_destructive_interference_size) Fiber::FiberContext {
|
||||||
fcontext_t fctx;
|
|
||||||
std::aligned_storage<kStackSize, kAlignSize>::type stack;
|
std::aligned_storage<kStackSize, kAlignSize>::type stack;
|
||||||
std::function<void()> proc;
|
std::function<void()> proc;
|
||||||
};
|
};
|
||||||
|
|
||||||
// static void SetPrevFiber(Fiber *fiber) noexcept { tls_prev_fiber = fiber; }
|
|
||||||
static Fiber *PrevFiber() noexcept { return tls_prev_fiber; }
|
|
||||||
|
|
||||||
void FiberEntry(fcontext_transfer_t t) {
|
void FiberEntry(fcontext_transfer_t t) {
|
||||||
SET_PREV_FCTX(t.fctx);
|
|
||||||
|
|
||||||
std::function<void()> *fn = static_cast<std::function<void()> *>(t.data);
|
std::function<void()> *fn = static_cast<std::function<void()> *>(t.data);
|
||||||
TILE_CHECK_NE(t.data, nullptr);
|
TILE_CHECK_NE(t.data, nullptr);
|
||||||
TILE_CHECK_NE(t.fctx, nullptr);
|
TILE_CHECK_NE(t.fctx, nullptr);
|
||||||
|
|
||||||
Fiber *self;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// From Resume()
|
// From Resume()
|
||||||
t = jump_fcontext(t.fctx, nullptr);
|
t = jump_fcontext(t.fctx, nullptr);
|
||||||
self = Fiber::Current();
|
auto self = reinterpret_cast<Fiber *>(t.data);
|
||||||
SET_PREV_FCTX(t.fctx);
|
self->caller_->ctx_ = t.fctx;
|
||||||
|
|
||||||
(*fn)();
|
(*fn)();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
TILE_LOG_ERROR("Exception caught in fiber: {}", e.what());
|
TILE_LOG_ERROR("Exception caught in fiber: {}", e.what());
|
||||||
}
|
}
|
||||||
TILE_CHECK_NE(t.fctx, nullptr);
|
|
||||||
|
|
||||||
if (self == Fiber::MasterFiber()) {
|
if (GetMasterFiber() != GetCurrentFiber()) {
|
||||||
// TILE_LOG_INFO("FiberEntry End. Resume to {}", t.fctx);
|
GetMasterFiber()->Resume();
|
||||||
jump_fcontext(t.fctx, nullptr);
|
|
||||||
} else {
|
} else {
|
||||||
// TILE_LOG_INFO("FiberEntry End. Resume to {}",
|
// master fiber end
|
||||||
// Fiber::MasterFiber()->ctx_->fctx);
|
jump_fcontext(t.fctx, GetMasterFiber());
|
||||||
Fiber::MasterFiber()->Resume();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fcontext_transfer_t FiberOnTop(fcontext_transfer_t t) {}
|
||||||
|
|
||||||
fcontext_t CreateFiber(void *stack, std::size_t stack_size,
|
fcontext_t CreateFiber(void *stack, std::size_t stack_size,
|
||||||
std::function<void()> *fn) {
|
std::function<void()> *fn) {
|
||||||
@ -75,14 +58,11 @@ fcontext_t CreateFiber(void *stack, std::size_t stack_size,
|
|||||||
return jump_fcontext(fctx, fn).fctx;
|
return jump_fcontext(fctx, fn).fctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
Fiber *Fiber::Current() noexcept { return tls_current_fiber; }
|
Fiber *GetCurrentFiber() noexcept { return tls_current_fiber; }
|
||||||
void Fiber::SetCurrent(Fiber *fiber) noexcept {
|
void SetUpCurrentFiber(Fiber *fiber) noexcept { tls_current_fiber = fiber; }
|
||||||
tls_prev_fiber = tls_current_fiber;
|
|
||||||
tls_current_fiber = fiber;
|
|
||||||
}
|
|
||||||
|
|
||||||
Fiber *Fiber::MasterFiber() noexcept { return tls_master_fiber; }
|
Fiber *GetMasterFiber() noexcept { return tls_master_fiber; }
|
||||||
void Fiber::SetMasterFiber(Fiber *fiber) noexcept { tls_master_fiber = fiber; }
|
void SetUpMasterFiber(Fiber *fiber) noexcept { tls_master_fiber = fiber; }
|
||||||
|
|
||||||
std::unique_ptr<Fiber> Fiber::Create(std::function<void()> proc) noexcept {
|
std::unique_ptr<Fiber> Fiber::Create(std::function<void()> proc) noexcept {
|
||||||
return std::unique_ptr<Fiber>(new Fiber(std::move(proc)));
|
return std::unique_ptr<Fiber>(new Fiber(std::move(proc)));
|
||||||
@ -90,45 +70,34 @@ std::unique_ptr<Fiber> Fiber::Create(std::function<void()> proc) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Fiber::Fiber(std::function<void()> proc)
|
Fiber::Fiber(std::function<void()> proc)
|
||||||
: ctx_(object_pool::Get<FiberContext>().Leak()) {
|
: data_(object_pool::Get<FiberContext>().Leak()) {
|
||||||
TILE_CHECK(proc);
|
TILE_CHECK(proc);
|
||||||
|
|
||||||
ctx_->proc = std::move(proc);
|
data_->proc = std::move(proc);
|
||||||
ctx_->fctx = CreateFiber(&ctx_->stack, kStackSize, &ctx_->proc);
|
ctx_ = CreateFiber(&data_->stack, kStackSize, &data_->proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
Fiber::~Fiber() {
|
Fiber::~Fiber() {
|
||||||
if (ctx_) {
|
if (data_) {
|
||||||
object_pool::Put<FiberContext>(ctx_.release());
|
object_pool::Put<FiberContext>(data_.release());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::Resume() {
|
void Fiber::Resume() {
|
||||||
TILE_CHECK(state_ != FiberState::Suspended);
|
auto caller = GetCurrentFiber();
|
||||||
TILE_CHECK(state_ != FiberState::Terminated);
|
TILE_CHECK_NE(caller, this, "Calling `ResumeOn()`, on self is undefined.");
|
||||||
TILE_CHECK_NE(ctx_->fctx, nullptr);
|
|
||||||
|
|
||||||
auto caller = Current();
|
auto t = jump_fcontext(ctx_, this);
|
||||||
TILE_CHECK_NE(caller, this, "Can't `Resume()` self");
|
caller = reinterpret_cast<Fiber *>(t.data);
|
||||||
if (caller != Fiber::MasterFiber() && this != Fiber::MasterFiber()) {
|
caller->ctx_ = t.fctx;
|
||||||
caller->state_ = FiberState::Suspended;
|
|
||||||
} else if (caller) {
|
|
||||||
caller->state_ = FiberState::Ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TILE_LOG_INFO("Resume from {} to {}", fmt::ptr(caller), fmt::ptr(this));
|
SetUpCurrentFiber(caller);
|
||||||
SetCurrent(this);
|
}
|
||||||
this->state_ = FiberState::Running;
|
|
||||||
auto caller_fctx =
|
|
||||||
jump_fcontext(internal::Exchange(ctx_->fctx, nullptr), nullptr).fctx;
|
|
||||||
if (Current() == this) {
|
|
||||||
// fiber terminated
|
|
||||||
state_ = FiberState::Terminated;
|
|
||||||
} else {
|
|
||||||
state_ = FiberState::Ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SET_PREV_FCTX(caller_fctx);
|
void Fiber::ResumeOn(std::function<void()> &&cb) noexcept {
|
||||||
|
auto caller = GetCurrentFiber();
|
||||||
|
TILE_CHECK_NE(caller, this, "Calling `ResumeOn()`, on self is undefined.");
|
||||||
|
ontop_fcontext(ctx_, this, FiberOnTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
@ -18,21 +18,16 @@ namespace detail {
|
|||||||
|
|
||||||
class Scheduler;
|
class Scheduler;
|
||||||
|
|
||||||
|
enum class FiberState {
|
||||||
|
Runnable,
|
||||||
|
Running,
|
||||||
|
Suspended,
|
||||||
|
Waiting,
|
||||||
|
Terminated,
|
||||||
|
};
|
||||||
|
|
||||||
class alignas(hardware_destructive_interference_size) Fiber {
|
class alignas(hardware_destructive_interference_size) Fiber {
|
||||||
public:
|
public:
|
||||||
enum class FiberState {
|
|
||||||
Runnable,
|
|
||||||
Running,
|
|
||||||
Suspended,
|
|
||||||
Waiting,
|
|
||||||
Terminated,
|
|
||||||
};
|
|
||||||
|
|
||||||
static Fiber *Current() noexcept;
|
|
||||||
static void SetCurrent(Fiber *fiber) noexcept;
|
|
||||||
static Fiber *MasterFiber() noexcept;
|
|
||||||
static void SetMasterFiber(Fiber *fiber) noexcept;
|
|
||||||
|
|
||||||
static std::unique_ptr<Fiber>
|
static std::unique_ptr<Fiber>
|
||||||
Create(std::function<void()> proc = nullptr) noexcept;
|
Create(std::function<void()> proc = nullptr) noexcept;
|
||||||
|
|
||||||
@ -43,24 +38,34 @@ public:
|
|||||||
Fiber(Fiber &&other) noexcept = default;
|
Fiber(Fiber &&other) noexcept = default;
|
||||||
Fiber &operator=(Fiber &&other) noexcept = default;
|
Fiber &operator=(Fiber &&other) noexcept = default;
|
||||||
|
|
||||||
// for `Scheduler`
|
|
||||||
void Resume();
|
void Resume();
|
||||||
|
void ResumeOn(std::function<void()> &&cb) noexcept;
|
||||||
void Yield();
|
void Yield();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TILE_FRIEND_TEST(Fiber, Base);
|
TILE_FRIEND_TEST(Fiber, Base);
|
||||||
friend Scheduler;
|
friend Scheduler;
|
||||||
|
|
||||||
friend void FiberEntry(struct fcontext_transfer);
|
|
||||||
struct FiberContext;
|
struct FiberContext;
|
||||||
friend class ::tile::PoolTraits<FiberContext>;
|
friend class ::tile::PoolTraits<FiberContext>;
|
||||||
|
|
||||||
|
friend void FiberEntry(struct fcontext_transfer);
|
||||||
|
friend struct fcontext_transfer FiberOnTop(struct fcontext_transfer);
|
||||||
|
|
||||||
Fiber(std::function<void()> proc = nullptr);
|
Fiber(std::function<void()> proc = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<FiberContext> ctx_;
|
std::unique_ptr<FiberContext> data_;
|
||||||
FiberState state_{FiberState::Runnable};
|
FiberState state_{FiberState::Runnable};
|
||||||
|
void *ctx_{nullptr};
|
||||||
|
Fiber *caller_{nullptr};
|
||||||
};
|
};
|
||||||
|
Fiber *GetCurrentFiber() noexcept;
|
||||||
|
Fiber *GetMasterFiber() noexcept;
|
||||||
|
|
||||||
|
void SetUpCurrentFiber(Fiber *fiber) noexcept;
|
||||||
|
void SetUpMasterFiber(Fiber *fiber) noexcept;
|
||||||
|
|
||||||
|
inline bool IsFiberContext() noexcept { return GetCurrentFiber() != nullptr; }
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace fiber
|
} // namespace fiber
|
||||||
|
@ -18,14 +18,14 @@ TEST(Fiber, Base) {
|
|||||||
master_fiber = Fiber::Create([&] {
|
master_fiber = Fiber::Create([&] {
|
||||||
TILE_LOG_INFO("master fiber");
|
TILE_LOG_INFO("master fiber");
|
||||||
// ASSERT_EQ(cnt, 0);
|
// ASSERT_EQ(cnt, 0);
|
||||||
ASSERT_EQ(Fiber::MasterFiber(), master_fiber.get());
|
ASSERT_EQ(GetMasterFiber(), master_fiber.get());
|
||||||
ASSERT_EQ(Fiber::Current(), master_fiber.get());
|
ASSERT_EQ(GetCurrentFiber(), master_fiber.get());
|
||||||
|
|
||||||
ASSERT_EQ(cnt, 0);
|
ASSERT_EQ(cnt, 0);
|
||||||
while (cnt < kMaxCnt) {
|
while (cnt < kMaxCnt) {
|
||||||
std::unique_ptr<Fiber> worker_fiber = Fiber::Create([&] {
|
std::unique_ptr<Fiber> worker_fiber = Fiber::Create([&] {
|
||||||
while (cnt < kMaxCnt) {
|
while (cnt < kMaxCnt) {
|
||||||
ASSERT_EQ(Fiber::Current(), worker_fiber.get());
|
ASSERT_EQ(GetCurrentFiber(), worker_fiber.get());
|
||||||
++cnt;
|
++cnt;
|
||||||
master_fiber->Resume();
|
master_fiber->Resume();
|
||||||
}
|
}
|
||||||
@ -35,15 +35,15 @@ TEST(Fiber, Base) {
|
|||||||
int old = cnt;
|
int old = cnt;
|
||||||
worker_fiber->Resume();
|
worker_fiber->Resume();
|
||||||
ASSERT_EQ(old + 1, cnt);
|
ASSERT_EQ(old + 1, cnt);
|
||||||
ASSERT_EQ(Fiber::Current(), master_fiber.get());
|
ASSERT_EQ(GetCurrentFiber(), master_fiber.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_EQ(cnt, kMaxCnt);
|
ASSERT_EQ(cnt, kMaxCnt);
|
||||||
});
|
});
|
||||||
|
|
||||||
Fiber::SetMasterFiber(master_fiber.get());
|
SetUpMasterFiber(master_fiber.get());
|
||||||
Fiber::SetCurrent(nullptr);
|
SetUpCurrentFiber(nullptr);
|
||||||
master_fiber->Resume();
|
master_fiber->Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,17 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "tile/fiber/detail/fiber.h"
|
||||||
|
|
||||||
namespace tile {
|
namespace tile {
|
||||||
namespace fiber {
|
namespace fiber {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class Scheduler {};
|
class Scheduler {
|
||||||
|
public:
|
||||||
|
void SwitchTo(Fiber *self, Fiber *to);
|
||||||
|
void Yield(Fiber *self);
|
||||||
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace fiber
|
} // namespace fiber
|
||||||
} // namespace tile
|
} // namespace tile
|
||||||
|
Loading…
x
Reference in New Issue
Block a user