12#include <asyncpp/detail/sanitizers.h>
15#ifndef ASYNCPP_FIBER_KEYWORDS
16#define ASYNCPP_FIBER_KEYWORDS 1
24#ifndef ASYNCPP_FIBER_USE_UCONTEXT
25#define ASYNCPP_FIBER_USE_UCONTEXT 0
28#if ASYNCPP_FIBER_USE_UCONTEXT
32#if !defined(MAP_ANON) && defined(__APPLE__)
33#define MAP_ANON 0x1000
36namespace asyncpp::detail {
37 struct stack_context {
44 inline bool fiber_allocate_stack(stack_context& ctx,
size_t size)
noexcept {
45 static size_t pagesize = sysconf(_SC_PAGESIZE);
48 const auto page_count = (size + pagesize - 1) / pagesize;
49 size = page_count * pagesize;
50 const auto alloc_size = size + pagesize * 2;
53 ::mmap(
nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_STACK, -1, 0);
54#elif defined(MAP_ANON)
55 void*
const stack = ::mmap(
nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
57 void*
const stack = ::mmap(
nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
59 if (stack == MAP_FAILED)
return false;
62 [[maybe_unused]]
auto res = ::mprotect(stack, pagesize, PROT_NONE);
64 res = ::mprotect(
static_cast<std::byte*
>(stack) + size + pagesize, pagesize, PROT_NONE);
67 ctx.stack =
static_cast<std::byte*
>(stack) + size + pagesize;
68 ctx.stack_size = size;
69 ctx.mmap_base = stack;
70 ctx.mmap_size = alloc_size;
74 inline bool fiber_deallocate_stack(stack_context& ctx) {
75 if (ctx.mmap_base ==
nullptr)
return true;
77 auto res = ::munmap(ctx.mmap_base, ctx.mmap_size);
81#if ASYNCPP_FIBER_USE_UCONTEXT
82 struct fiber_context {
88 inline bool fiber_makecontext(fiber_context* ctx,
const stack_context& stack,
void (*fn)(
void* arg),
void* arg) {
89 static void (*
const wrapper)(uint32_t, uint32_t, uint32_t, uint32_t) = [](uint32_t fn_hi, uint32_t fn_low,
90 uint32_t arg_hi, uint32_t arg_low) {
91 uintptr_t fn = (
static_cast<uintptr_t
>(fn_hi) << 32) |
static_cast<uintptr_t
>(fn_low);
92 uintptr_t arg = (
static_cast<uintptr_t
>(arg_hi) << 32) |
static_cast<uintptr_t
>(arg_low);
93 reinterpret_cast<void (*)(
void*)
>(fn)(
reinterpret_cast<void*
>(arg));
95 getcontext(&ctx->context);
96 ctx->context.uc_link =
nullptr;
97 ctx->context.uc_stack.ss_sp =
reinterpret_cast<decltype(ctx-
>context.uc_stack.ss_sp)>(
98 reinterpret_cast<uintptr_t
>(stack.stack) - stack.stack_size);
99 ctx->context.uc_stack.ss_size = stack.stack_size;
100 makecontext(&ctx->context,
reinterpret_cast<void (*)()
>(wrapper), 4,
101 (
reinterpret_cast<uintptr_t
>(fn) >> 32) & 0xffffffff,
reinterpret_cast<uintptr_t
>(fn) & 0xffffffff,
102 (
reinterpret_cast<uintptr_t
>(arg) >> 32) & 0xffffffff,
103 reinterpret_cast<uintptr_t
>(arg) & 0xffffffff);
107 inline bool fiber_swapcontext(fiber_context* out, fiber_context* in) {
108 swapcontext(&out->context, &in->context);
112 inline bool fiber_destroy_context(fiber_context* ctx) {
113 memset(ctx, 0,
sizeof(fiber_context));
117 struct fiber_context {
123 inline bool fiber_makecontext(fiber_context* ctx,
const stack_context& stack,
void (*entry_fn)(
void* arg),
125 ctx->stack = stack.stack;
126 ctx->stack_size = stack.stack_size;
130 auto sp =
reinterpret_cast<uintptr_t*
>(
reinterpret_cast<uintptr_t
>(stack.stack) &
static_cast<uintptr_t
>(~15L));
139 uintptr_t stack_guard;
149 uintptr_t :
sizeof(uintptr_t) * 8;
152 static_assert(
sizeof(stack_frame) ==
sizeof(uintptr_t) * 10);
153 sp -=
sizeof(stack_frame) /
sizeof(uintptr_t);
154 memset(sp, 0,
reinterpret_cast<uintptr_t
>(stack.stack) -
reinterpret_cast<uintptr_t
>(sp));
155 auto frame =
reinterpret_cast<stack_frame*
>(sp);
156 frame->ret_addr =
reinterpret_cast<uintptr_t
>(entry_fn);
157 frame->param =
reinterpret_cast<uintptr_t
>(arg);
158 assert((
reinterpret_cast<uintptr_t
>(sp) % 16) == 0xc);
159#elif defined(__x86_64__)
167 uintptr_t stack_guard;
180 static_assert(
sizeof(stack_frame) ==
sizeof(uintptr_t) * 10);
181 sp -=
sizeof(stack_frame) /
sizeof(uintptr_t);
182 memset(sp, 0,
reinterpret_cast<uintptr_t
>(stack.stack) -
reinterpret_cast<uintptr_t
>(sp));
183 auto frame =
reinterpret_cast<stack_frame*
>(sp);
184 frame->ret_addr =
reinterpret_cast<uintptr_t
>(entry_fn);
185 frame->param =
reinterpret_cast<uintptr_t
>(arg);
186#elif defined(__arm__)
190#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
191 uintptr_t d8_d15[16];
212#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
213 static_assert(
sizeof(stack_frame) ==
sizeof(uintptr_t) * 28);
215 static_assert(
sizeof(stack_frame) ==
sizeof(uintptr_t) * 11);
217 sp -=
sizeof(stack_frame) /
sizeof(uintptr_t);
218 memset(sp, 0,
reinterpret_cast<uintptr_t
>(stack.stack) -
reinterpret_cast<uintptr_t
>(sp));
219 auto frame =
reinterpret_cast<stack_frame*
>(sp);
220 frame->ret_addr =
reinterpret_cast<uintptr_t
>(entry_fn);
221 frame->param =
reinterpret_cast<uintptr_t
>(arg);
222#elif defined(__arm64__) || defined(__aarch64__)
227 uintptr_t x19_x29[11];
238 static_assert(
sizeof(stack_frame) ==
sizeof(uintptr_t) * 22);
239 sp -=
sizeof(stack_frame) /
sizeof(uintptr_t);
240 memset(sp, 0,
reinterpret_cast<uintptr_t
>(stack.stack) -
reinterpret_cast<uintptr_t
>(sp));
241 auto frame =
reinterpret_cast<stack_frame*
>(sp);
242 frame->ret_addr =
reinterpret_cast<uintptr_t
>(entry_fn);
243 frame->param =
reinterpret_cast<uintptr_t
>(arg);
244#elif defined(__s390x__)
259 static_assert(
sizeof(stack_frame) ==
sizeof(uintptr_t) * 18);
260 sp -=
sizeof(stack_frame) /
sizeof(uintptr_t);
261 memset(sp, 0,
reinterpret_cast<uintptr_t
>(stack.stack) -
reinterpret_cast<uintptr_t
>(sp));
262 auto frame =
reinterpret_cast<stack_frame*
>(sp);
263 frame->ret_addr =
reinterpret_cast<uintptr_t
>(entry_fn);
264 frame->param =
reinterpret_cast<uintptr_t
>(arg);
265#elif defined(__powerpc64__)
274 uintptr_t r14_31[18];
275 uintptr_t fr14_31[18];
280 static_assert(
sizeof(stack_frame) ==
sizeof(uintptr_t) * 42);
281 sp -=
sizeof(stack_frame) /
sizeof(uintptr_t);
282 memset(sp, 0,
reinterpret_cast<uintptr_t
>(stack.stack) -
reinterpret_cast<uintptr_t
>(sp));
283 auto frame =
reinterpret_cast<stack_frame*
>(sp);
287 frame->r12 =
reinterpret_cast<uintptr_t
>(entry_fn);
288 frame->ret_addr =
reinterpret_cast<uintptr_t
>(entry_fn);
289 frame->param =
reinterpret_cast<uintptr_t
>(arg);
291#error "Unsupported architecture."
293 ctx->current_sp = sp;
297 __attribute__((naked, noinline))
inline void fiber_swap_stack(
void** current_pointer_out,
void* dest_pointer) {
301 asm(
"leal -0x1c(%esp), %esp\n"
305#ifdef SAVE_TLS_STACK_PROTECTOR
306 "movl %gs:0x14, %ecx\n"
307 "movl %ecx, 0x8(%esp)\n"
309 "movl %edi, 0xc(%esp)\n"
310 "movl %esi, 0x10(%esp)\n"
311 "movl %ebx, 0x14(%esp)\n"
312 "movl %ebp, 0x18(%esp)\n"
314 "movl 0x20(%esp), %ecx\n"
315 "movl %esp, (%ecx)\n"
317 "mov 0x24(%esp), %esi\n"
322#ifdef SAVE_TLS_STACK_PROTECTOR
323 "movl 0x8(%esp), %edx\n"
324 "movl %edx, %gs:0x14\n"
326 "movl 0xc(%esp), %edi\n"
327 "movl 0x10(%esp), %esi\n"
328 "movl 0x14(%esp), %ebx\n"
329 "movl 0x18(%esp), %ebp\n"
331 "leal 0x1c(%esp), %esp\n"
334#elif defined(__x86_64__)
335#if !(defined(__LP64__) || defined(__LLP64__))
337#error "Non-native pointer size."
341 asm(
"leaq -0x48(%rsp), %rsp\n"
345#ifdef SAVE_TLS_STACK_PROTECTOR
346 "movq %fs:0x28, %rcx\n"
347 "movq %rcx, 0x8(%rsp)\n"
349 "movq %r12, 0x10(%rsp)\n"
350 "movq %r13, 0x18(%rsp)\n"
351 "movq %r14, 0x20(%rsp)\n"
352 "movq %r15, 0x28(%rsp)\n"
353 "movq %rbx, 0x30(%rsp)\n"
354 "movq %rbp, 0x38(%rsp)\n"
356 "movq %rsp, (%rdi)\n"
362#ifdef SAVE_TLS_STACK_PROTECTOR
363 "movq 0x8(%rsp), %rcx\n"
364 "movq %rcx, %fs:28\n"
367 "movq 0x10(%rsp), %r12\n"
368 "movq 0x18(%rsp), %r13\n"
369 "movq 0x20(%rsp), %r14\n"
370 "movq 0x28(%rsp), %r15\n"
371 "movq 0x30(%rsp), %rbx\n"
372 "movq 0x38(%rsp), %rbp\n"
373 "movq 0x40(%rsp), %rdi\n"
375 "leaq 0x48(%rsp), %rsp\n"
378#elif defined(__arm__)
386#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
388 "vstmia sp, {d8-d15}\n"
396#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
397 "vldmia sp, {d8-d15}\n"
407#elif defined(__arm64__) || defined(__aarch64__)
412 "sub sp, sp, #0xb0\n"
413 "stp d8, d9, [sp, #0x00]\n"
414 "stp d10, d11, [sp, #0x10]\n"
415 "stp d12, d13, [sp, #0x20]\n"
416 "stp d14, d15, [sp, #0x30]\n"
417 "stp x19, x20, [sp, #0x40]\n"
418 "stp x21, x22, [sp, #0x50]\n"
419 "stp x23, x24, [sp, #0x60]\n"
420 "stp x25, x26, [sp, #0x70]\n"
421 "stp x27, x28, [sp, #0x80]\n"
422 "stp x29, x30, [sp, #0x90]\n"
429 "ldp d8, d9, [sp, #0x00]\n"
430 "ldp d10, d11, [sp, #0x10]\n"
431 "ldp d12, d13, [sp, #0x20]\n"
432 "ldp d14, d15, [sp, #0x30]\n"
433 "ldp x19, x20, [sp, #0x40]\n"
434 "ldp x21, x22, [sp, #0x50]\n"
435 "ldp x23, x24, [sp, #0x60]\n"
436 "ldp x25, x26, [sp, #0x70]\n"
437 "ldp x27, x28, [sp, #0x80]\n"
438 "ldp x29, x30, [sp, #0x90]\n"
439 "ldr x0, [sp, #0xa0]\n"
440 "add sp, sp, #0xb0\n"
443#elif defined(__s390x__)
448 "aghi %r15, -(8*18)\n"
450 "stmg %r6, %r14, 64(%r15)\n"
451 "std %f8, (8*0)(%r15)\n"
452 "std %f9, (8*1)(%r15)\n"
453 "std %f10, (8*2)(%r15)\n"
454 "std %f11, (8*3)(%r15)\n"
455 "std %f12, (8*4)(%r15)\n"
456 "std %f13, (8*5)(%r15)\n"
457 "std %f14, (8*6)(%r15)\n"
458 "std %f15, (8*7)(%r15)\n"
464 "lmg %r6, %r14, (8*8)(%r15)\n"
465 "ld %f8, (8*0)(%r15)\n"
466 "ld %f9, (8*1)(%r15)\n"
467 "ld %f10, (8*2)(%r15)\n"
468 "ld %f11, (8*3)(%r15)\n"
469 "ld %f12, (8*4)(%r15)\n"
470 "ld %f13, (8*5)(%r15)\n"
471 "ld %f14, (8*6)(%r15)\n"
472 "ld %f15, (8*7)(%r15)\n"
473 "lg %r2, (8*17)(%r15)\n"
475 "aghi %r15, (8*18)\n"
478#elif defined(__powerpc64__)
481 asm(
"addis %r2, %r12, .TOC.-fiber_swap_stack@ha\n"
482 "addi %r2, %r2, .TOC.-fiber_swap_stack@l\n"
483 ".localentry fiber_swap_stack, . - fiber_swap_stack\n"
484 "addi %r1, %r1, -(42*8)\n"
487 "std %r2, (8*0)(%r1)\n"
488 "std %r3, (8*1)(%r1)\n"
489 "std %r12, (8*2)(%r1)\n"
490 "std %r14, (8*3)(%r1)\n"
491 "std %r15, (8*4)(%r1)\n"
492 "std %r16, (8*5)(%r1)\n"
493 "std %r17, (8*6)(%r1)\n"
494 "std %r18, (8*7)(%r1)\n"
495 "std %r19, (8*8)(%r1)\n"
496 "std %r20, (8*9)(%r1)\n"
497 "std %r21, (8*10)(%r1)\n"
498 "std %r22, (8*11)(%r1)\n"
499 "std %r23, (8*12)(%r1)\n"
500 "std %r24, (8*13)(%r1)\n"
501 "std %r25, (8*14)(%r1)\n"
502 "std %r26, (8*15)(%r1)\n"
503 "std %r27, (8*16)(%r1)\n"
504 "std %r28, (8*17)(%r1)\n"
505 "std %r29, (8*18)(%r1)\n"
506 "std %r30, (8*19)(%r1)\n"
507 "std %r31, (8*20)(%r1)\n"
508 "stfd %f14, (8*21)(%r1)\n"
509 "stfd %f15, (8*22)(%r1)\n"
510 "stfd %f16, (8*23)(%r1)\n"
511 "stfd %f17, (8*24)(%r1)\n"
512 "stfd %f18, (8*25)(%r1)\n"
513 "stfd %f19, (8*26)(%r1)\n"
514 "stfd %f20, (8*27)(%r1)\n"
515 "stfd %f21, (8*28)(%r1)\n"
516 "stfd %f22, (8*29)(%r1)\n"
517 "stfd %f23, (8*30)(%r1)\n"
518 "stfd %f24, (8*31)(%r1)\n"
519 "stfd %f25, (8*32)(%r1)\n"
520 "stfd %f26, (8*33)(%r1)\n"
521 "stfd %f27, (8*34)(%r1)\n"
522 "stfd %f28, (8*35)(%r1)\n"
523 "stfd %f29, (8*36)(%r1)\n"
524 "stfd %f30, (8*37)(%r1)\n"
525 "stfd %f31, (8*38)(%r1)\n"
527 "std %r0, (8*39)(%r1)\n"
529 "std %r0, (8*40)(%r1)\n"
530 "std %r0, (8*41)(%r1)\n"
536 "ld %r2, (8*0)(%r1)\n"
537 "ld %r3, (8*1)(%r1)\n"
538 "ld %r12, (8*2)(%r1)\n"
539 "ld %r14, (8*3)(%r1)\n"
540 "ld %r15, (8*4)(%r1)\n"
541 "ld %r16, (8*5)(%r1)\n"
542 "ld %r17, (8*6)(%r1)\n"
543 "ld %r18, (8*7)(%r1)\n"
544 "ld %r19, (8*8)(%r1)\n"
545 "ld %r20, (8*9)(%r1)\n"
546 "ld %r21, (8*10)(%r1)\n"
547 "ld %r22, (8*11)(%r1)\n"
548 "ld %r23, (8*12)(%r1)\n"
549 "ld %r24, (8*13)(%r1)\n"
550 "ld %r25, (8*14)(%r1)\n"
551 "ld %r26, (8*15)(%r1)\n"
552 "ld %r27, (8*16)(%r1)\n"
553 "ld %r28, (8*17)(%r1)\n"
554 "ld %r29, (8*18)(%r1)\n"
555 "ld %r30, (8*19)(%r1)\n"
556 "ld %r31, (8*20)(%r1)\n"
557 "lfd %f14,(8*21)(%r1)\n"
558 "lfd %f15,(8*22)(%r1)\n"
559 "lfd %f16,(8*23)(%r1)\n"
560 "lfd %f17,(8*24)(%r1)\n"
561 "lfd %f18,(8*25)(%r1)\n"
562 "lfd %f19,(8*26)(%r1)\n"
563 "lfd %f20,(8*27)(%r1)\n"
564 "lfd %f21,(8*28)(%r1)\n"
565 "lfd %f22,(8*29)(%r1)\n"
566 "lfd %f23,(8*30)(%r1)\n"
567 "lfd %f24,(8*31)(%r1)\n"
568 "lfd %f25,(8*32)(%r1)\n"
569 "lfd %f26,(8*33)(%r1)\n"
570 "lfd %f27,(8*34)(%r1)\n"
571 "lfd %f28,(8*35)(%r1)\n"
572 "lfd %f29,(8*36)(%r1)\n"
573 "lfd %f30,(8*37)(%r1)\n"
574 "lfd %f31,(8*38)(%r1)\n"
575 "ld %r0, (8*39)(%r1)\n"
577 "ld %r0, (8*40)(%r1)\n"
579 "ld %r0, (8*41)(%r1)\n"
581 "addi %r1, %r1, (8*42)\n"
585#error "Unsupported architecture."
589 inline bool fiber_swapcontext(fiber_context* out_ctx, fiber_context* in_ctx) {
590 fiber_swap_stack(&out_ctx->current_sp, in_ctx->current_sp);
594 inline bool fiber_destroy_context(fiber_context* ctx) {
595 memset(ctx, 0,
sizeof(fiber_context));
604namespace asyncpp::detail {
605 struct stack_context {
610 inline bool fiber_allocate_stack(stack_context& ctx,
size_t size)
noexcept {
611 static size_t pagesize = []() {
613 ::GetSystemInfo(&si);
614 return static_cast<size_t>(si.dwPageSize);
618 const auto page_count = (size + pagesize - 1) / pagesize;
619 size = page_count * pagesize;
620 ctx.stack_size = size;
624 inline bool fiber_deallocate_stack(stack_context& ctx)
noexcept {
return true; }
626 struct fiber_context {
628 void (*start_fn)(
void* arg);
632 inline bool fiber_makecontext(fiber_context* ctx,
const stack_context& stack,
void (*entry_fn)(
void* arg),
634 static void (*wrapper)(LPVOID) = [](LPVOID param) {
635 auto ctx =
static_cast<fiber_context*
>(param);
636 ctx->start_fn(ctx->start_arg);
638 ctx->fiber_handle = CreateFiber(stack.stack_size, wrapper, ctx);
639 if (ctx->fiber_handle == NULL)
return false;
640 ctx->start_fn = entry_fn;
641 ctx->start_arg = arg;
645 inline bool fiber_swapcontext(fiber_context* out, fiber_context* in) {
646#if (_WIN32_WINNT > 0x0600)
647 if (::IsThreadAFiber() == FALSE) {
648 out->fiber_handle = ::ConvertThreadToFiber(
nullptr);
650 out->fiber_handle = ::GetCurrentFiber();
653 out->fiber_handle = ::ConvertThreadToFiber(
nullptr);
654 if (out->fiber_handle == NULL) {
655 if (::GetLastError() != ERROR_ALREADY_FIBER)
return false;
656 out->fiber_handle = ::GetCurrentFiber();
659 if (out->fiber_handle == NULL)
return false;
660 SwitchToFiber(in->fiber_handle);
664 inline bool fiber_destroy_context(fiber_context* ctx) {
665 DeleteFiber(ctx->fiber_handle);
666 ctx->fiber_handle = NULL;
673 template<
typename TReturn>
676namespace asyncpp::detail {
677 struct fiber_handle_base {
679 void (*resume_cb)(fiber_handle_base*) =
nullptr;
680 void (*destroy_cb)(fiber_handle_base*) =
nullptr;
682 detail::fiber_context main_context{};
683 detail::fiber_context fiber_context{};
684 detail::stack_context fiber_stack{};
686 bool (*suspend_handler)(
void* ptr, coroutine_handle<> hndl) =
nullptr;
687 void* suspend_handler_ptr =
nullptr;
688 std::exception_ptr suspend_exception =
nullptr;
690 coroutine_handle<> continuation{};
692 bool want_destroy =
false;
693 bool was_started =
false;
695 void* asan_handle =
nullptr;
696 const void* asan_parent_stack =
nullptr;
697 size_t asan_parent_stack_size{};
700 void* tsan_parent =
nullptr;
701 void* tsan_fiber =
nullptr;
703#if ASYNCPP_HAS_VALGRIND
704 unsigned valgrind_id = 0;
708#if defined(ASYNCPP_SO_COMPAT)
709 extern thread_local fiber_handle_base* g_current_fiber;
711 inline static thread_local fiber_handle_base* g_current_fiber =
nullptr;
714#if defined(ASYNCPP_SO_COMPAT_IMPL)
715 thread_local fiber_handle_base* g_current_fiber =
nullptr;
718 struct fiber_destroy_requested_exception {};
720 template<
typename FN>
721 static coroutine_handle<> make_fiber_handle(
size_t stack_size, FN&& cbfn) {
722 struct handle : fiber_handle_base {
724 explicit handle(FN&& cbfn) : function(std::forward<FN>(cbfn)) {}
726 static_assert(offsetof(handle, resume_cb) == 0);
728 auto hndl =
new handle(std::forward<FN>(cbfn));
729 hndl->resume_cb = [](fiber_handle_base* hndl) {
730 auto old = std::exchange(g_current_fiber, hndl);
731 while (hndl->resume_cb !=
nullptr) {
734 __sanitizer_start_switch_fiber(&asan_handle, hndl->fiber_stack.mmap_base, hndl->fiber_stack.mmap_size);
737 hndl->tsan_parent = __tsan_get_current_fiber();
738 __tsan_switch_to_fiber(hndl->tsan_fiber, 0);
740 detail::fiber_swapcontext(&hndl->main_context, &hndl->fiber_context);
742 const void* fiber_stack;
743 size_t fiber_stack_size;
744 __sanitizer_finish_switch_fiber(asan_handle, &fiber_stack, &fiber_stack_size);
745 assert(fiber_stack == hndl->fiber_stack.mmap_base);
746 assert(fiber_stack_size == hndl->fiber_stack.mmap_size);
748 if (hndl->suspend_handler) {
749 auto handler = std::exchange(hndl->suspend_handler,
nullptr);
750 auto ptr = std::exchange(hndl->suspend_handler_ptr,
nullptr);
752 if (handler(ptr, coroutine_handle<>::from_address(
static_cast<void*
>(hndl))))
break;
753 }
catch (...) { hndl->suspend_exception = std::current_exception(); }
756 g_current_fiber = old;
757 if (hndl->resume_cb ==
nullptr && hndl->continuation !=
nullptr) { hndl->continuation.resume(); }
759 hndl->destroy_cb = [](fiber_handle_base* hndl) {
761 if (hndl->resume_cb !=
nullptr && hndl->was_started) {
762 hndl->want_destroy =
true;
763 hndl->resume_cb(hndl);
764 assert(hndl->resume_cb ==
nullptr);
767 __tsan_destroy_fiber(hndl->tsan_fiber);
769#if ASYNCPP_HAS_VALGRIND
770 VALGRIND_STACK_DEREGISTER(hndl->valgrind_id);
772 detail::fiber_destroy_context(&hndl->fiber_context);
773 detail::fiber_deallocate_stack(hndl->fiber_stack);
774 delete static_cast<handle*
>(hndl);
777 hndl->tsan_fiber = __tsan_create_fiber(0);
779 if (!detail::fiber_allocate_stack(hndl->fiber_stack, stack_size)) {
781 __tsan_destroy_fiber(hndl->tsan_fiber);
784 throw std::bad_alloc();
786 if (!detail::fiber_makecontext(
787 &hndl->fiber_context, hndl->fiber_stack,
789 auto hndl = static_cast<handle*>(ptr);
791 __sanitizer_finish_switch_fiber(hndl->asan_handle, &hndl->asan_parent_stack,
792 &hndl->asan_parent_stack_size);
794 hndl->was_started = true;
797 } catch (
const fiber_destroy_requested_exception&) {}
798 hndl->resume_cb =
nullptr;
800 __sanitizer_start_switch_fiber(
nullptr, hndl->asan_parent_stack, hndl->asan_parent_stack_size);
803 assert(hndl->tsan_fiber == __tsan_get_current_fiber());
804 __tsan_switch_to_fiber(hndl->tsan_parent, 0);
806 detail::fiber_swapcontext(&hndl->fiber_context, &hndl->main_context);
811 __tsan_destroy_fiber(hndl->tsan_fiber);
813 fiber_deallocate_stack(hndl->fiber_stack);
815 throw std::bad_alloc();
817#if ASYNCPP_HAS_VALGRIND
819 VALGRIND_STACK_REGISTER(hndl->fiber_stack.mmap_base,
820 static_cast<uint8_t*
>(hndl->fiber_stack.mmap_base) + hndl->fiber_stack.mmap_size);
822 return coroutine_handle<>::from_address(
static_cast<void*
>(hndl));
826 static auto fiber_await(T&& awaiter) {
829 if (std::uncaught_exceptions() != 0) std::terminate();
830 if (!
static_cast<bool>(awaiter.await_ready())) {
840 auto hndl = detail::g_current_fiber;
841 assert(hndl !=
nullptr);
843 assert(__builtin_frame_address(0) > hndl->fiber_stack.mmap_base);
844 assert(__builtin_frame_address(0) <
845 (
static_cast<uint8_t*
>(hndl->fiber_stack.mmap_base) + hndl->fiber_stack.mmap_size));
847 using AwaitResult =
decltype(awaiter.await_suspend(std::declval<coroutine_handle<>>()));
848 hndl->suspend_handler = [](
void* ptr, coroutine_handle<> hndl) {
849 if constexpr (std::is_same_v<AwaitResult, void>) {
850 static_cast<T*
>(ptr)->await_suspend(hndl);
852 }
else if constexpr (std::is_same_v<AwaitResult, bool>) {
853 return static_cast<T*
>(ptr)->await_suspend(hndl);
855 static_cast<T*
>(ptr)->await_suspend(hndl).resume();
859 hndl->suspend_handler_ptr = &awaiter;
862 __sanitizer_start_switch_fiber(&asan_handle, hndl->asan_parent_stack, hndl->asan_parent_stack_size);
865 assert(hndl->tsan_fiber == __tsan_get_current_fiber());
866 __tsan_switch_to_fiber(hndl->tsan_parent, 0);
868 detail::fiber_swapcontext(&hndl->fiber_context, &hndl->main_context);
870 __sanitizer_finish_switch_fiber(asan_handle, &hndl->asan_parent_stack, &hndl->asan_parent_stack_size);
872 if (hndl->suspend_exception !=
nullptr)
873 std::rethrow_exception(std::exchange(hndl->suspend_exception,
nullptr));
874 if (hndl->want_destroy) {
876 throw fiber_destroy_requested_exception{};
879 return awaiter.await_resume();
882 struct fib_await_helper {
885 auto operator=(T&& awaiter) {
886 return fiber_await(std::forward<T>(awaiter));
898 coroutine_handle<> m_handle;
913 template<
typename FN>
914 explicit fiber(FN&& function,
size_t stack_size = 262144)
915 requires(!std::is_same_v<FN, fiber>)
916 : m_handle(detail::make_fiber_handle(stack_size, std::forward<FN>(function))) {}
918 constexpr fiber() noexcept : m_handle(
nullptr) {}
921 if (m_handle) m_handle.destroy();
924 fiber(
fiber&& other) noexcept : m_handle(std::exchange(other.m_handle,
nullptr)) {}
927 if (&other !=
this) {
928 if (m_handle) m_handle.destroy();
929 m_handle = std::exchange(other.m_handle,
nullptr);
945 return detail::fiber_await(std::forward<
decltype(awaiter)>(awaiter));
953 if (!m_handle)
throw std::logic_error(
"empty fiber");
955 coroutine_handle<> m_handle;
956 [[nodiscard]]
bool await_ready()
const noexcept {
return m_handle.done(); }
957 [[nodiscard]] coroutine_handle<> await_suspend(coroutine_handle<> hdl)
const {
958 auto ctx =
static_cast<detail::fiber_handle_base*
>(m_handle.address());
959 if (ctx->continuation !=
nullptr)
throw std::logic_error(
"already awaited");
960 ctx->continuation = hdl;
963 constexpr void await_resume()
const noexcept {}
965 return awaiter{m_handle};
972 auto operator co_await() {
return await(); }
978 template<
typename TReturn>
980 std::unique_ptr<std::optional<TReturn>> m_result{};
996 template<
typename FN>
997 explicit fiber(FN&& function,
size_t stack_size = 262144)
998 requires(!std::is_same_v<FN, fiber>)
999 : m_result(std::make_unique<std::optional<TReturn>>()),
1000 m_base([function = std::forward<FN>(function), res = m_result.get()]() { res->emplace(function()); },
1005 fiber(
fiber&& other) noexcept : m_result(std::move(other.m_result)), m_base(std::move(other.m_base)) {}
1008 if (&other !=
this) {
1009 m_base = std::move(other.m_base);
1010 m_result = std::move(other.m_result);
1024 template<
typename T>
1026 return detail::fiber_await(std::forward<T>(awaiter));
1034 if (!m_result)
throw std::logic_error(
"empty fiber");
1036 decltype(m_base.operator
co_await()) m_awaiter;
1037 std::optional<TReturn>* m_result;
1038 [[nodiscard]]
bool await_ready()
const noexcept {
return m_awaiter.await_ready(); }
1039 [[nodiscard]] coroutine_handle<> await_suspend(coroutine_handle<> hdl)
const {
1040 return m_awaiter.await_suspend(hdl);
1042 auto await_resume()
const noexcept {
1043 assert(m_result->has_value());
1044 return m_result->value();
1047 return awaiter{m_base.
await(), m_result.get()};
1055 if (!m_result)
throw std::logic_error(
"empty fiber");
1057 decltype(m_base.operator
co_await()) m_awaiter;
1058 std::optional<TReturn>* m_result;
1059 [[nodiscard]]
bool await_ready()
const noexcept {
return m_awaiter.await_ready(); }
1060 [[nodiscard]] coroutine_handle<> await_suspend(coroutine_handle<> hdl)
const {
1061 return m_awaiter.await_suspend(hdl);
1063 auto await_resume()
const noexcept {
1064 assert(m_result->has_value());
1065 return std::move(m_result->value());
1068 return awaiter{m_base.
await(), m_result.get()};
1075 auto operator co_await() & {
return await(); }
1080 auto operator co_await() && {
return await(); }
1083 template<
typename FN>
1084 fiber(FN&&) -> fiber<std::invoke_result_t<FN>>;
1087#if ASYNCPP_FIBER_KEYWORDS
1088#define fib_await (asyncpp::detail::fib_await_helper{}) =
~fiber()
Destructor.
Definition fiber.h:920
auto await()
Get an awaitable for this fiber.
Definition fiber.h:952
constexpr fiber() noexcept
Construct an empty fiber handle.
Definition fiber.h:918
fiber & operator=(fiber &&other) noexcept
Move constructor. The moved from handle will be empty.
Definition fiber.h:926
fiber(FN &&function, size_t stack_size=262144)
Construct a new fiber for the specified entry function.
Definition fiber.h:914
static auto fiber_await(T &&awaiter)
Await a standard C++20 coroutine awaitable.
Definition fiber.h:944
fiber(fiber &&other) noexcept
Move constructor. The moved from handle will be empty.
Definition fiber.h:924
Fiber class providing a stackfull coroutine.
Definition fiber.h:979
static auto fiber_await(T &&awaiter)
Await a standard C++20 coroutine awaitable.
Definition fiber.h:1025
constexpr fiber() noexcept=default
Construct an empty fiber handle.
auto await() &&
Get an awaitable for this fiber.
Definition fiber.h:1054
fiber & operator=(fiber &&other) noexcept
Move constructor. The moved from handle will be empty.
Definition fiber.h:1007
auto await() &
Get an awaitable for this fiber.
Definition fiber.h:1033
fiber(FN &&function, size_t stack_size=262144)
Construct a new fiber for the specified entry function.
Definition fiber.h:997
Provides a consistent import interface for coroutine, experimental/coroutine or a best effort fallbac...