@ -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 ( i s_thread_fiber, " Exitting non main thread fiber " ) ;
if ( ! i s_thread_fiber) {
ASSERT_MSG ( i mpl- > i s_thread_fiber, " Exitting non main thread fiber " ) ;
if ( ! i mpl- > i s_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 - > i s_thread_fiber = true ;
fiber - > i mpl- > i s_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 - > i s_thread_fiber = true ;
fiber - > impl- > guard. lock ( ) ;
fiber - > i mpl- > i s_thread_fiber = true ;
return fiber ;
}