Skip to content

Commit

Permalink
add zcontext code
Browse files Browse the repository at this point in the history
  • Loading branch information
microcai committed Dec 18, 2024
1 parent 898c979 commit e2d6da2
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 25 deletions.
17 changes: 17 additions & 0 deletions example/echo_client/echo_client_stackfull.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,25 @@ static void echo_client(HANDLE iocp_handle, const char* lp_server_addr)

FiberOVERLAPPED ov;


auto result = WSAConnectEx(sock, (const SOCKADDR*) &server_addr, INET_ADDRSTRLEN, 0, 0, 0, &ov.ov);

float c = ov.last_error + 1;

c*=99;
printf("c = %f before\n", c);

ov.last_error = WSAGetLastError();

if (!(!result && ov.last_error != WSA_IO_PENDING))
{
get_overlapped_result(&ov);
}

c /= 99;

printf("c = %f\n", c);

if (ov.last_error)
{
printf("connection failed\n");
Expand Down Expand Up @@ -113,7 +124,13 @@ int main(int argc, char* argv[])

#endif

float cc = 8;
cc *= argc;
printf("cc = %f before\n", cc);
create_detached_coroutine(echo_client, iocp_handle, (const char*)(argc == 2 ? argv[1] : "127.0.0.1"));
cc *= argc;

printf("cc = %f\n", cc);

run_event_loop(iocp_handle);
CloseHandle(iocp_handle);
Expand Down
10 changes: 9 additions & 1 deletion µasync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

option(DISABLE_BOOST_CONTEXT "use ucontext even if boost is found" OFF)
option(DISABLE_UCONTEXT "disable ucontext and use setjmp/longjmp" OFF)
option(USE_ZCONTEXT "use zcontext api to do context switch" OFF)

cmake_policy(SET CMP0167 NEW)
find_package(Boost 1.86 COMPONENTS context)
Expand All @@ -24,4 +25,11 @@ endif()

if (DISABLE_UCONTEXT)
target_compile_definitions(uasync INTERFACE -DDISABLE_UCONTEXT=1)
endif()
endif()

if (USE_ZCONTEXT)
enable_language(ASM)
add_library(zcontext asm/zcontext_x86_64.S)
target_compile_definitions(uasync INTERFACE -DUSE_ZCONTEXT=1)
target_link_libraries(uasync INTERFACE zcontext)
endif()
65 changes: 65 additions & 0 deletions µasync/asm/zcontext_x86_64.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

.align 16
.global zcontext_swap
// RDI = to save sp to
// RSI = to load sp from
// RDX = argument
zcontext_swap: // void* zcontext_swap(zcontext_t* from, zcontext_t* to, void* argument)

// 保存非易失性寄存器
push %rbp
push %rbx
push %r10
push %r12
push %r13
push %r14
push %r15
// 保存浮点上下文
sub $16, %rsp
fnstcw 0(%rsp)
stmxcsr 8(%rsp)

// 切换栈指针
mov %rsp, (%rdi)
mov (%rsi), %rsp

// 恢复浮点上下文
fldcw (%rsp)
ldmxcsr 8(%rsp)
add $16, %rsp
// 恢复非易失性寄存器
pop %r15
pop %r14
pop %r13
pop %r12
pop %r10
pop %rbx
pop %rbp

// 返回 argument
mov %rdx, %rax
mov (%rsp), %rcx
retq
.align 16


.align 16
zcontext_entry_point:
mov %r15, %rdi
call *%r14
hlt

.align 16
.global zcontext_setup
// RDI = target
// RSI = func
// RDX = argument
zcontext_setup: // void zcontext_setup(zcontext_t* target, void (*func)(void*arg), void* argument)
movq (%rdi), %rax
movq %rdx, -64(%rax)
subq $80, %rax
movq %rsi, 24(%rax)
movq zcontext_entry_point@GOTPCREL(%rip), %rcx;
movq %rcx, 72(%rax)
movq %rax, (%rdi)
ret
116 changes: 92 additions & 24 deletions µasync/include/universal_fiber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#define USE_SETJMP
#elif defined(DISABLE_UCONTEXT)
#define USE_SETJMP
#else
#elif !defined(USE_ZCONTEXT)
#define USE_UCONTEXT
#endif

Expand All @@ -46,6 +46,8 @@
#endif
#elif defined(USE_SETJMP)
#include <setjmp.h>
#elif defined(USE_ZCONTEXT)
#include "zcontext.h"
#endif

#ifdef __linux__
Expand All @@ -72,6 +74,8 @@ typedef struct FiberOVERLAPPED
ucontext_t target;
#elif defined (USE_SETJMP)
jmp_buf target_jmp;
#elif defined (USE_ZCONTEXT)
zcontext_t target;
#endif

DWORD byte_transfered;
Expand All @@ -80,6 +84,12 @@ typedef struct FiberOVERLAPPED
BOOL resultOk;
} FiberOVERLAPPED;

struct HelperStack
{
alignas(64) char sp[448];
alignas(64) char sp_top[1];
};

struct FiberContext
{
unsigned long long sp[
Expand All @@ -94,6 +104,8 @@ struct FiberContext
alignas(64) void* func_ptr; // NOTE: &func_ptr 相当于栈顶
#if defined (USE_UCONTEXT)
ucontext_t ctx;
#elif defined (USE_ZCONTEXT)
zcontext_t ctx;
#endif
};

Expand Down Expand Up @@ -145,15 +157,10 @@ inline thread_local LPVOID __please_delete_me = NULL;
#elif defined (USE_UCONTEXT)
inline thread_local ucontext_t* __current_yield_ctx = NULL;
#elif defined (USE_SETJMP)

struct HelperStack
{
alignas(64) char sp[448];
alignas(64) char sp_top[1];
};

inline thread_local jmp_buf __current_jump_buf;
inline thread_local FiberContext* __please_delete_me;
#elif defined (USE_ZCONTEXT)
inline thread_local zcontext_t* __current_yield_zctx = NULL;
#endif
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Expand Down Expand Up @@ -213,6 +220,17 @@ inline DWORD get_overlapped_result(FiberOVERLAPPED* ov)
WSASetLastError(ov->last_error);
return ov->byte_transfered;
}
#elif defined (USE_ZCONTEXT)

inline DWORD get_overlapped_result(FiberOVERLAPPED* ov)
{
assert(__current_yield_zctx && "get_overlapped_result should be called by a ucontext based coroutine!");

zcontext_swap(&ov->target, __current_yield_zctx, 0);

WSASetLastError(ov->last_error);
return ov->byte_transfered;
}

#endif

Expand Down Expand Up @@ -263,6 +281,17 @@ inline void process_overlapped_event(OVERLAPPED* _ov,
FiberContextAlloctor{}.deallocate(__please_delete_me);
__please_delete_me = NULL;
}
#elif defined (USE_ZCONTEXT)
zcontext_t self;
zcontext_t* old = __current_yield_zctx;
__current_yield_zctx = &self;
auto __please_delete_me = zcontext_swap(&self, &ovl_res->target, 0);
__current_yield_zctx = old;
if (__please_delete_me)
{
FiberContextAlloctor{}.deallocate((FiberContext*)__please_delete_me);
__please_delete_me = NULL;
}
#endif
}

Expand Down Expand Up @@ -321,7 +350,7 @@ inline void exit_event_loop_when_empty(HANDLE iocp_handle)
// different stackfull implementations
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#ifdef USE_SETJMP
#if defined (USE_SETJMP) || defined (USE_ZCONTEXT)
inline void execute_on_new_stack(void* new_sp, void (*jump_target)(void*), void * param)
{
#if defined (__x86_64__)
Expand Down Expand Up @@ -396,12 +425,12 @@ static inline void WINAPI __coroutine_entry_point(LPVOID param)
#endif // defined(USE_WINFIBER)


#if defined (USE_UCONTEXT) || defined (USE_SETJMP) || defined (USE_FCONTEXT)
#if defined (USE_UCONTEXT) || defined (USE_SETJMP) || defined (USE_FCONTEXT) || defined (USE_ZCONTEXT)

template<typename... Args>
#if defined (USE_FCONTEXT)
inline void __coroutine_entry_point(boost::context::detail::transfer_t arg)
#else //if defined (USE_UCONTEXT) || defined (USE_SETJMP)
#else //if defined (USE_UCONTEXT) || defined (USE_SETJMP) || defined (USE_ZCONTEXT)
static inline void __coroutine_entry_point(FiberContext* ctx)
#endif
{
Expand All @@ -418,7 +447,6 @@ static inline void __coroutine_entry_point(FiberContext* ctx)
{
typedef void (* real_func_type)(Args...);
auto real_func_ptr = reinterpret_cast<real_func_type>(ctx->func_ptr);

std::apply(real_func_ptr, std::move(*fiber_args));
}

Expand Down Expand Up @@ -451,12 +479,15 @@ static inline void __coroutine_entry_point(FiberContext* ctx)
#elif defined(USE_SETJMP)
__please_delete_me = ctx;
longjmp(__current_jump_buf, 1);
#elif defined (USE_ZCONTEXT)
zcontext_t self;
zcontext_swap(&self, __current_yield_zctx, ctx);
#endif

}
#endif

#if defined (USE_FCONTEXT) || defined (USE_UCONTEXT) || defined (USE_SETJMP)
#if defined (USE_FCONTEXT) || defined (USE_UCONTEXT) || defined (USE_ZCONTEXT)

template<typename... Args>
inline void create_detached_coroutine(void (*func_ptr)(Args...), Args... args)
Expand All @@ -482,39 +513,76 @@ inline void create_detached_coroutine(void (*func_ptr)(Args...), Args... args)
__current_yield_ctx = &self;

ucontext_t* new_ctx = & new_fiber_ctx->ctx;

auto stack_size = (new_fiber_ctx->sp_top - new_fiber_ctx->sp) * sizeof (unsigned long long);

getcontext(new_ctx);
new_ctx->uc_stack.ss_sp = new_fiber_ctx->sp;
new_ctx->uc_stack.ss_flags = 0;
new_ctx->uc_stack.ss_size = sizeof(new_fiber_ctx->sp);
new_ctx->uc_stack.ss_size = stack_size;
new_ctx->uc_link = __current_yield_ctx;

typedef void (*__func)(void);
makecontext(new_ctx, (__func)__coroutine_entry_point<Args...>, 1, new_fiber_ctx);

swapcontext(&self, new_ctx);
__current_yield_ctx = old;
# elif defined (USE_SETJMP)
#elif defined (USE_ZCONTEXT)

// setup a new stack, and jump to __coroutine_entry_point
typedef void(*entry_point_type)(FiberContext*);

entry_point_type entry_func = & __coroutine_entry_point<Args...>;
unsigned long long * new_sp = new_fiber_ctx->sp_top;

zcontext_t self;
zcontext_t* old = __current_yield_zctx;
__current_yield_zctx = &self;

new_fiber_ctx->ctx.sp = new_sp;

zcontext_setup(&new_fiber_ctx->ctx, reinterpret_cast<void (*)(void*)>(entry_func), new_fiber_ctx);
auto __please_delete_me = zcontext_swap(&self, &new_fiber_ctx->ctx, 0);
__current_yield_zctx = old;

if (__please_delete_me)
{
FiberContextAlloctor{}.deallocate((FiberContext*) __please_delete_me);
__please_delete_me = NULL;
}
# endif
}

#endif //defined (USE_SETJMP) || defined (USE_ZCONTEXT) || defined (USE_ZCONTEXT)

#if defined (USE_SETJMP)

template<typename... Args>
inline void create_detached_coroutine(void (*func_ptr)(Args...), Args... args)
{
using arg_tuple = std::tuple<Args...>;

++ out_standing_coroutines;

FiberContext* new_fiber_ctx = FiberContextAlloctor{}.allocate();
new_fiber_ctx->func_ptr = reinterpret_cast<void*>(func_ptr);
// placement new argument passed to function
new (new_fiber_ctx->sp) arg_tuple{std::forward<Args>(args)...};

// jmp_buf_fiber_entry(&fiber_param);
if (!setjmp(__current_jump_buf))
{
// setup a new stack, and jump to __coroutine_entry_point
typedef void(*entry_point_type)(FiberContext*);

entry_point_type entry_func = & __coroutine_entry_point<Args...>;
void * new_sp = &new_fiber_ctx->sp_top;
void * new_sp = new_fiber_ctx->sp_top;

execute_on_new_stack(new_sp, reinterpret_cast<void (*)(void*)>(entry_func), new_fiber_ctx);
}
if (__please_delete_me)
{
free(__please_delete_me);
__please_delete_me = NULL;
}

# endif
}

#endif
#endif // defined (USE_SETJMP)

#if defined(USE_WINFIBER)

Expand Down
11 changes: 11 additions & 0 deletions µasync/include/zcontext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

#pragma once

struct zcontext_t
{
void* sp;// pointer to active stack buttom
};

void* zcontext_swap(zcontext_t* from, zcontext_t* to, void* argument) asm("zcontext_swap");

void zcontext_setup(zcontext_t* target, void (*func)(void*arg), void* argument) asm("zcontext_setup");

0 comments on commit e2d6da2

Please sign in to comment.