From 7cd949b492a1f781e726fe0d0d02342397a1b69c Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Tue, 21 Apr 2026 11:41:08 -0700 Subject: [PATCH 01/14] fix up page size estimation code. Add planning doc for simplification. --- src/yafl.c | 108 ++++++++++++++++++----------------------------------- 1 file changed, 37 insertions(+), 71 deletions(-) diff --git a/src/yafl.c b/src/yafl.c index e035ea6..96e0cbb 100644 --- a/src/yafl.c +++ b/src/yafl.c @@ -54,10 +54,7 @@ extern yafl_transfer_t jump_fcontext(yafl_t const to, void *vp); #define FCONTEXT_STACK_WATERMARK 0xA5 #define FCONTEXT_STACK_ALIGNMENT 16 -typedef enum { - FCONTEXT_ALLOC_MALLOC, - FCONTEXT_ALLOC_VMEM -} yafl_alloc_type_t; +typedef enum { FCONTEXT_ALLOC_MALLOC, FCONTEXT_ALLOC_VMEM } yafl_alloc_type_t; /* Internal fiber structure */ struct yafl_fiber { @@ -73,13 +70,13 @@ struct yafl_fiber { bool watermark_filled; /* Context tracking */ - yafl_t context; /* Fiber's saved context */ - yafl_t resumer_context; /* Context to return to */ + yafl_t context; /* Fiber's saved context */ + yafl_t resumer_context; /* Context to return to */ /* User entry and result */ yafl_fiber_fn user_entry; void *cached_result; - void *pending_arg; /* Argument for next resume */ + void *pending_arg; /* Argument for next resume */ }; /* Typedef for internal use */ @@ -99,16 +96,14 @@ static size_t get_page_size(void) { return (size_t)si.dwPageSize; #else long page_size = sysconf(_SC_PAGE_SIZE); - if (page_size <= 0) { - return 4096; - } + if(page_size <= 0) { return 4096; } return (size_t)page_size; #endif } static void *align_stack_pointer(void *ptr) { uintptr_t addr = (uintptr_t)ptr; - return (void *)(addr & ~(FCONTEXT_STACK_ALIGNMENT - 1)); + return (void *)(addr & ~((uintptr_t)FCONTEXT_STACK_ALIGNMENT - 1)); } /* ======================================================================== @@ -147,27 +142,20 @@ static void fiber_entry_trampoline(yafl_transfer_t t) { * Fiber Creation * ======================================================================== */ -extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, - yafl_stack_flags_t flags) { +extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, yafl_stack_flags_t flags) { /* Validate fiber function is not NULL */ - if (fiber_fn == NULL) { - return NULL; - } + if(fiber_fn == NULL) { return NULL; } /* Validate exactly one allocation type is set */ int alloc_flags = flags & (YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_VMEM); - if (alloc_flags != YAFL_STACK_FLAGS_MALLOC && alloc_flags != YAFL_STACK_FLAGS_VMEM) { - return NULL; - } + if(alloc_flags != YAFL_STACK_FLAGS_MALLOC && alloc_flags != YAFL_STACK_FLAGS_VMEM) { return NULL; } bool use_vmem = (flags & YAFL_STACK_FLAGS_VMEM) != 0; bool use_watermark = (flags & YAFL_STACK_FLAGS_WATERMARK) != 0; /* Allocate fiber structure */ yafl_fiber_t *fiber = malloc(sizeof(yafl_fiber_t)); - if (fiber == NULL) { - return NULL; - } + if(fiber == NULL) { return NULL; } /* Initialize fiber structure */ fiber->magic = FCONTEXT_FIBER_MAGIC; @@ -181,12 +169,10 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size fiber->resumer_context = NULL; /* Use default stack size if not specified */ - if (stack_size == 0) { - stack_size = FCONTEXT_DEFAULT_STACK_SIZE; - } + if(stack_size == 0) { stack_size = FCONTEXT_DEFAULT_STACK_SIZE; } /* Allocate stack based on allocation type */ - if (use_vmem) { + if(use_vmem) { size_t page_size = get_page_size(); size_t stack_with_overhead = stack_size + 256; size_t aligned_stack_size = ((stack_with_overhead + page_size - 1) / page_size) * page_size; @@ -196,28 +182,28 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size void *region = NULL; #ifdef _WIN32 region = VirtualAlloc(NULL, total_size, MEM_RESERVE, PAGE_NOACCESS); - if (region == NULL) { + if(region == NULL) { free(fiber); return NULL; } void *stack_base = (char *)region + guard_size; - if (!VirtualAlloc(stack_base, aligned_stack_size, MEM_COMMIT, PAGE_READWRITE)) { + if(!VirtualAlloc(stack_base, aligned_stack_size, MEM_COMMIT, PAGE_READWRITE)) { VirtualFree(region, 0, MEM_RELEASE); free(fiber); return NULL; } #else region = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (region == MAP_FAILED) { + if(region == MAP_FAILED) { free(fiber); return NULL; } - if (mprotect(region, guard_size, PROT_NONE) == -1) { + if(mprotect(region, guard_size, PROT_NONE) == -1) { munmap(region, total_size); free(fiber); return NULL; } - if (mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { + if(mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { munmap(region, total_size); free(fiber); return NULL; @@ -228,7 +214,7 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size void *stack_region_end = (char *)stack_base + aligned_stack_size; void *stack_top = (char *)stack_region_end - 256; stack_top = align_stack_pointer(stack_top); - size_t actual_stack_size = (char *)stack_top - (char *)stack_base; + size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)stack_base); fiber->stack_region = region; fiber->stack_total_size = total_size; @@ -238,7 +224,7 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size /* malloc allocation */ size_t allocated_size = stack_size + 256; void *block = malloc(allocated_size); - if (block == NULL) { + if(block == NULL) { free(fiber); return NULL; } @@ -247,7 +233,7 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size void *stack_top = (char *)block_end - 256; stack_top = align_stack_pointer(stack_top); - size_t actual_stack_size = (char *)stack_top - (char *)block; + size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)block); fiber->stack_region = block; fiber->stack_total_size = allocated_size; @@ -257,8 +243,8 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size /* Initialize the low-level context with trampoline as entry */ fiber->context = make_fcontext(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); - if (fiber->context == NULL) { - if (use_vmem) { + if(fiber->context == NULL) { + if(use_vmem) { #ifdef _WIN32 VirtualFree(fiber->stack_region, 0, MEM_RELEASE); #else @@ -272,12 +258,12 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size } /* Apply watermark if requested */ - if (use_watermark) { + if(use_watermark) { memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); /* Reinitialize context after watermark (overwrites filled area) */ fiber->context = make_fcontext(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); - if (fiber->context == NULL) { - if (use_vmem) { + if(fiber->context == NULL) { + if(use_vmem) { #ifdef _WIN32 VirtualFree(fiber->stack_region, 0, MEM_RELEASE); #else @@ -300,19 +286,13 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { /* Validation */ - if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { - return NULL; - } + if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return NULL; } /* If complete, return cached result (idempotent) */ - if (fiber->status == YAFL_FIBER_STATUS_COMPLETE) { - return fiber->cached_result; - } + if(fiber->status == YAFL_FIBER_STATUS_COMPLETE) { return fiber->cached_result; } /* Cannot resume running fiber */ - if (fiber->status == YAFL_FIBER_STATUS_RUNNING) { - return NULL; - } + if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return NULL; } /* Store argument for delivery */ fiber->pending_arg = arg; @@ -338,9 +318,7 @@ extern void *yafl_fiber_suspend(void *result) { yafl_fiber_t *current = tls_current_fiber; /* Must be in a fiber */ - if (current == NULL) { - return NULL; - } + if(current == NULL) { return NULL; } /* Update status */ current->status = YAFL_FIBER_STATUS_SUSPENDED; @@ -362,24 +340,18 @@ extern void *yafl_fiber_suspend(void *result) { * ======================================================================== */ extern yafl_fiber_status_t yafl_fiber_status(yafl_fiber_t *fiber) { - if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { - return YAFL_FIBER_STATUS_ERR; - } + if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return YAFL_FIBER_STATUS_ERR; } return fiber->status; } extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { - if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC || !fiber->watermark_filled) { - return 0; - } + if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC || !fiber->watermark_filled) { return 0; } /* Scan from stack base for watermark bytes */ unsigned char *stack_base = (unsigned char *)fiber->stack_top - fiber->stack_size; size_t unused = 0; - while (unused < fiber->stack_size && stack_base[unused] == FCONTEXT_STACK_WATERMARK) { - unused++; - } + while(unused < fiber->stack_size && stack_base[unused] == FCONTEXT_STACK_WATERMARK) { unused++; } return fiber->stack_size - unused; } @@ -389,19 +361,15 @@ extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { * ======================================================================== */ extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { - if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { - return; - } + if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return; } /* Cannot destroy running fiber */ - if (fiber->status == YAFL_FIBER_STATUS_RUNNING) { - return; - } + if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return; } /* Free stack based on allocation type */ - if (fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { + if(fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { free(fiber->stack_region); - } else if (fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { + } else if(fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { #ifdef _WIN32 VirtualFree(fiber->stack_region, 0, MEM_RELEASE); #else @@ -418,6 +386,4 @@ extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { * Utilities * ======================================================================== */ -extern size_t yafl_get_page_size(void) { - return get_page_size(); -} +extern size_t yafl_get_page_size(void) { return get_page_size(); } From 06aa297059a236a4c797ebaa41f0ee583326c665 Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Tue, 21 Apr 2026 11:53:15 -0700 Subject: [PATCH 02/14] Add additional signal, SIGILL, when running ASAN/UBSAN sanitizers. --- test_struct_size | Bin 33440 -> 0 bytes test_struct_size.c | 13 ------------- tests/test_yafl_guard.c | 4 ++-- 3 files changed, 2 insertions(+), 15 deletions(-) delete mode 100755 test_struct_size delete mode 100644 test_struct_size.c diff --git a/test_struct_size b/test_struct_size deleted file mode 100755 index b739de0995df6994baa82f39f4ad5f685588b513..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33440 zcmeI5Urby@6vk)o^0!NcS^=w7_CXt3Nol||eQ2AdLIfp1S{{gfn1y8*ZVI~%yK8}x z^j6whtTAi@Nnea@G*OK9p=k{<8cAr>2cv~(+ZZE#X{0e2Nk|NhSkIZAy_e$OVW{$3H zYVq1ohQ~UmX`+;^b#1L{(`vnMZA@rCo5E~Vl4?@Mg8eb=P?xUfr-`h0Q%_-^zsqh} z!m`ISkTM#JcSP+_y525&IqMN4)!29H}qPHm1$ z84n*Z!=2I$ha$>f%kNLE*QQ0-PuI{X)$u-@Ba^bWrnN@C(A;z&xf?!x2&h*rt#?z9 zjad|tN!b-~Y{Px^@@TGVzT|rCi5wcJeb<52bJp0<^m1KoL+XrJX?2m$?Y#a-)^O@P zFNDSQ@h zo3)KP^?mG9quMRj@n?A^X^JJn@&B$sZXj`c?^ zA$ae$YM*&=dx}iG|2XYIvPc!b-{ao2m{~b$OsouxM4qtre=uA&`NeeE`p1pUd(yNjN)vPjGUHZhg20WPAC{9VNB2$8_avs=^7;;{xybM>3#06M z)Eo>(Lgk@AB+Nf2`lGZ54$0V7?;h{7gYk7j=i_ELR$Y}Dx7W4z^h5$OHgGtYIf*qg zqaBe*Px@TK-K>!3m00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1papdUQ%o!<15$sN^j@2-p&i1 z`7ZHPQ)3_9;_gdD^jd>_dAN`6x(=y~ONvK%z;U;@MR$rDvbS3X`-6daEGWB!?VZ6s zk>A^HhQ)fJCZwhpy3|eMs-6-G7E)n+FE!W4_f=DGm&)>NUb=}sQ(h{0XIV5=wYxtT zOp(mnip<6|FPYKwe%Cp49HV`k=% z(cG4OZ|L&S;K?69KT|(a@&3c+=YIJ0)Z%!^eW4;}=Te|7w7A|g`TcM2&K`X2*6p*# w<%NgU|F8A$9Gj~;Qol65Fum}md#-rn8;u|C-TT( - -int main() { - printf("sizeof(yafl_stack_t) = %zu\n", sizeof(yafl_stack_t)); - printf("sizeof(uint32_t) = %zu\n", sizeof(uint32_t)); - printf("sizeof(yafl_alloc_type_t) = %zu\n", sizeof(yafl_alloc_type_t)); - printf("sizeof(size_t) = %zu\n", sizeof(size_t)); - printf("sizeof(bool) = %zu\n", sizeof(bool)); - printf("sizeof(void*) = %zu\n", sizeof(void *)); - printf("sizeof(yafl_t) = %zu\n", sizeof(yafl_t)); - return 0; -} diff --git a/tests/test_yafl_guard.c b/tests/test_yafl_guard.c index 6475d43..744ee24 100644 --- a/tests/test_yafl_guard.c +++ b/tests/test_yafl_guard.c @@ -181,8 +181,8 @@ int main(int argc, char *argv[]) { fprintf(stderr, "[main] child killed by signal %d\n", sig); fflush(stderr); - /* Expect SIGSEGV (11) or SIGBUS (7) for guard page fault */ - if (sig == SIGSEGV || sig == SIGBUS) { + /* Expect SIGSEGV (11) or SIGBUS (7) SIGILL (4) for guard page fault */ + if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL) { fprintf(stderr, "[main] guard page successfully detected stack overflow\n"); fflush(stderr); } else { From 2ba09ffd0d17fade39a7f1a8e4c34709456634ee Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 21 Apr 2026 19:00:47 +0000 Subject: [PATCH 03/14] Update code coverage badges [skip ci] --- docs/coverage_linux_aarch64.svg | 2 +- docs/coverage_linux_x86_64.svg | 2 +- docs/coverage_macos_aarch64.svg | 2 +- docs/coverage_macos_x86_64.svg | 2 +- docs/coverage_windows_x86_64.svg | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/coverage_linux_aarch64.svg b/docs/coverage_linux_aarch64.svg index f737a95..2997e32 100644 --- a/docs/coverage_linux_aarch64.svg +++ b/docs/coverage_linux_aarch64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 86.1% + 86.2% diff --git a/docs/coverage_linux_x86_64.svg b/docs/coverage_linux_x86_64.svg index f737a95..2997e32 100644 --- a/docs/coverage_linux_x86_64.svg +++ b/docs/coverage_linux_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 86.1% + 86.2% diff --git a/docs/coverage_macos_aarch64.svg b/docs/coverage_macos_aarch64.svg index efd1237..f70ec20 100644 --- a/docs/coverage_macos_aarch64.svg +++ b/docs/coverage_macos_aarch64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 94.3% + 95.3% diff --git a/docs/coverage_macos_x86_64.svg b/docs/coverage_macos_x86_64.svg index efd1237..f70ec20 100644 --- a/docs/coverage_macos_x86_64.svg +++ b/docs/coverage_macos_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 94.3% + 95.3% diff --git a/docs/coverage_windows_x86_64.svg b/docs/coverage_windows_x86_64.svg index 0d26668..930a2b2 100644 --- a/docs/coverage_windows_x86_64.svg +++ b/docs/coverage_windows_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 87% + 89% From 2767c67abca7a08cd0b7b019b1c59f5246b388ad Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Tue, 28 Apr 2026 10:48:18 -0700 Subject: [PATCH 04/14] Remove the old lower level symmetric coroutine API. It is not used or exposed. Co-authored-by: Copilot --- CMakeLists.txt | 3 +- tests/test_yafl_api.c | 360 ------------------------------- tests/test_yafl_symmetric.c | 211 ------------------ tests/test_yafl_watermark.c | 122 +++++++++++ tests/test_yafl_watermark_mmap.c | 80 ------- 5 files changed, 124 insertions(+), 652 deletions(-) delete mode 100644 tests/test_yafl_api.c delete mode 100644 tests/test_yafl_symmetric.c create mode 100644 tests/test_yafl_watermark.c delete mode 100644 tests/test_yafl_watermark_mmap.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 29a86eb..6214449 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,6 +213,7 @@ set(TESTS test_yafl_suspend_resume test_yafl_guard test_yafl_many + test_yafl_watermark ) # Create static library @@ -233,7 +234,7 @@ endforeach() # Testing # ============================================================================ -enable_testing() +include(CTest) # Ensure all test-related output goes to the build directory only set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED TRUE) diff --git a/tests/test_yafl_api.c b/tests/test_yafl_api.c deleted file mode 100644 index dc8ac94..0000000 --- a/tests/test_yafl_api.c +++ /dev/null @@ -1,360 +0,0 @@ -/** - * test_yafl_api.c - * Comprehensive API test - * - * Tests all API functions, error conditions, and edge cases. - * - * Copyright Kyle Hayes (2026) - * Distributed under the Boost Software License, Version 1.0. - */ - -#include -#include -#include -#include -#include -#include - -#include "yafl.h" - -static int call_count = 0; - -void *simple_fiber(void *data) { - call_count++; - fprintf(stderr, "[fiber] called with data: %p\n", data); - fflush(stderr); - return (void *)0xABCD; -} - -void *null_data_fiber(void *data) { - fprintf(stderr, "[fiber] received NULL data: %p\n", data); - fflush(stderr); - assert(data == NULL); - return NULL; -} - -void *yielding_fiber(void *data) { - fprintf(stderr, "[fiber] first call, data: %p\n", data); - fflush(stderr); - assert(data == (void *)0x1); - - void *result = yafl_fiber_yield((void *)0x2); - fprintf(stderr, "[fiber] resumed with: %p\n", result); - fflush(stderr); - assert(result == (void *)0x3); - - return (void *)0x4; -} - -void *multi_yield_fiber(void *data) { - (void)data; - fprintf(stderr, "[multi_yield] starting\n"); - fflush(stderr); - - /* Multiple consecutive yields */ - for(int i = 0; i < 5; i++) { - fprintf(stderr, "[multi_yield] yield %d\n", i); - fflush(stderr); - yafl_fiber_yield((void *)(uintptr_t)(0x100 + i)); - } - - fprintf(stderr, "[multi_yield] finishing\n"); - fflush(stderr); - return (void *)0x999; -} - -int main(void) { - fprintf(stderr, "=== yafl Comprehensive API Test ===\n"); - fflush(stderr); - - /* Test: yafl_get_page_size() */ - fprintf(stderr, "\n[test] yafl_get_page_size()\n"); - fflush(stderr); - size_t page_size = yafl_get_page_size(); - fprintf(stderr, "[test] page size: %zu bytes\n", page_size); - fflush(stderr); - assert(page_size > 0); - assert(page_size >= 4096); - - /* Test: Explicit thread conversion */ - fprintf(stderr, "\n[test] explicit thread conversion\n"); - fflush(stderr); - assert(!yafl_fiber_is_thread_converted()); - yafl_fiber_t *thread_fiber = yafl_fiber_convert_thread(); - assert(thread_fiber != NULL); - assert(yafl_fiber_is_thread_converted()); - assert(yafl_fiber_current() == thread_fiber); - fprintf(stderr, "[test] thread converted: %p\n", (void *)thread_fiber); - fflush(stderr); - - /* Test: Idempotent thread conversion */ - fprintf(stderr, "\n[test] idempotent thread conversion\n"); - fflush(stderr); - yafl_fiber_t *thread_fiber2 = yafl_fiber_convert_thread(); - assert(thread_fiber2 == thread_fiber); - fprintf(stderr, "[test] same fiber returned: %p\n", (void *)thread_fiber2); - fflush(stderr); - - /* Test: Default stack size (0) */ - fprintf(stderr, "\n[test] default stack size\n"); - fflush(stderr); - yafl_fiber_t *fiber_default = yafl_fiber_create_vmem(0, simple_fiber); - assert(fiber_default != NULL); - size_t default_size = yafl_fiber_get_stack_size(fiber_default); - fprintf(stderr, "[test] default stack size: %zu bytes\n", default_size); - fflush(stderr); - assert(default_size > 0); - - /* Test: Fiber state transitions */ - fprintf(stderr, "\n[test] fiber state transitions\n"); - fflush(stderr); - assert(yafl_fiber_get_state(fiber_default) == FCONTEXT_FIBER_CREATED); - fprintf(stderr, "[test] state: CREATED\n"); - fflush(stderr); - - void *result = yafl_fiber_switch(fiber_default, (void *)0x99); - assert(yafl_fiber_get_state(fiber_default) == FCONTEXT_FIBER_FINISHED); - fprintf(stderr, "[test] state: FINISHED\n"); - fflush(stderr); - assert(result == (void *)0xABCD); - assert(call_count == 1); - - /* Test: Idempotent switch to finished fiber */ - fprintf(stderr, "\n[test] idempotent switch to finished fiber\n"); - fflush(stderr); - result = yafl_fiber_switch(fiber_default, (void *)0x88); - assert(result == (void *)0xABCD); /* Cached result */ - assert(call_count == 1); /* Not called again */ - fprintf(stderr, "[test] returned cached result, fiber not re-entered\n"); - fflush(stderr); - - /* Test: Destroy returns cached result */ - fprintf(stderr, "\n[test] destroy returns result\n"); - fflush(stderr); - void *destroy_result = yafl_fiber_destroy(fiber_default); - assert(destroy_result == (void *)0xABCD); - fprintf(stderr, "[test] destroy returned: %p\n", destroy_result); - fflush(stderr); - - /* Test: NULL data handling */ - fprintf(stderr, "\n[test] NULL as valid data\n"); - fflush(stderr); - yafl_fiber_t *fiber_null = yafl_fiber_create_malloc(16 * 1024, null_data_fiber); - assert(fiber_null != NULL); - result = yafl_fiber_switch(fiber_null, NULL); - assert(result == NULL); /* NULL is valid return value */ - fprintf(stderr, "[test] NULL data passed and returned correctly\n"); - fflush(stderr); - yafl_fiber_destroy(fiber_null); - - /* Test: yafl_fiber_get_caller() and yafl_fiber_current() */ - fprintf(stderr, "\n[test] get_caller() and current()\n"); - fflush(stderr); - assert(yafl_fiber_current() == thread_fiber); - assert(yafl_fiber_get_caller() == NULL); /* Thread fiber has no caller */ - fprintf(stderr, "[test] current: %p, caller: NULL\n", (void *)yafl_fiber_current()); - fflush(stderr); - - /* Test: Yield and state changes */ - fprintf(stderr, "\n[test] yield and state changes\n"); - fflush(stderr); - yafl_fiber_t *fiber_yield = yafl_fiber_create_vmem(16 * 1024, yielding_fiber); - assert(fiber_yield != NULL); - assert(yafl_fiber_get_state(fiber_yield) == FCONTEXT_FIBER_CREATED); - - result = yafl_fiber_switch(fiber_yield, (void *)0x1); - assert(result == (void *)0x2); - assert(yafl_fiber_get_state(fiber_yield) == FCONTEXT_FIBER_SUSPENDED); - fprintf(stderr, "[test] fiber yielded, state: SUSPENDED\n"); - fflush(stderr); - - result = yafl_fiber_switch(fiber_yield, (void *)0x3); - assert(result == (void *)0x4); - assert(yafl_fiber_get_state(fiber_yield) == FCONTEXT_FIBER_FINISHED); - fprintf(stderr, "[test] fiber finished, state: FINISHED\n"); - fflush(stderr); - yafl_fiber_destroy(fiber_yield); - - /* Test: Error conditions - NULL fiber */ - fprintf(stderr, "\n[test] error: switch to NULL fiber\n"); - fflush(stderr); - result = yafl_fiber_switch(NULL, NULL); - assert(result == NULL); - fprintf(stderr, "[test] NULL fiber handled correctly\n"); - fflush(stderr); - - /* Test: Error conditions - get_state on NULL */ - fprintf(stderr, "\n[test] error: get_state on NULL\n"); - fflush(stderr); - yafl_fiber_state_t state = yafl_fiber_get_state(NULL); - assert(state == FCONTEXT_FIBER_CREATED); /* Safe default */ - fprintf(stderr, "[test] NULL fiber state handled correctly\n"); - fflush(stderr); - - /* Test: Error conditions - destroy NULL */ - fprintf(stderr, "\n[test] error: destroy NULL\n"); - fflush(stderr); - destroy_result = yafl_fiber_destroy(NULL); - assert(destroy_result == NULL); - fprintf(stderr, "[test] destroy NULL handled correctly\n"); - fflush(stderr); - - /* Test: Watermark error conditions */ - fprintf(stderr, "\n[test] watermark error conditions\n"); - fflush(stderr); - yafl_fiber_t *fiber_wm = yafl_fiber_create_vmem(16 * 1024, simple_fiber); - assert(fiber_wm != NULL); - - /* Get usage without fill */ - size_t usage = yafl_fiber_get_stack_usage(fiber_wm); - assert(usage == SIZE_MAX); /* Error return */ - fprintf(stderr, "[test] usage without fill: SIZE_MAX\n"); - fflush(stderr); - - /* Fill watermark */ - bool filled = yafl_fiber_fill_watermark(fiber_wm); - assert(filled); - - /* Try to fill again (should fail, already filled) */ - filled = yafl_fiber_fill_watermark(fiber_wm); - assert(!filled); - fprintf(stderr, "[test] watermark cannot be re-filled\n"); - fflush(stderr); - - yafl_fiber_switch(fiber_wm, NULL); - usage = yafl_fiber_get_stack_usage(fiber_wm); - assert(usage != SIZE_MAX && usage > 0); - fprintf(stderr, "[test] usage after fill: %zu bytes\n", usage); - fflush(stderr); - yafl_fiber_destroy(fiber_wm); - - /* Test: Create fiber with NULL entry */ - fprintf(stderr, "\n[test] error: create fiber with NULL entry\n"); - fflush(stderr); - yafl_fiber_t *fiber_null_entry = yafl_fiber_create_vmem(16 * 1024, NULL); - assert(fiber_null_entry == NULL); - fiber_null_entry = yafl_fiber_create_malloc(16 * 1024, NULL); - assert(fiber_null_entry == NULL); - fprintf(stderr, "[test] NULL entry rejected correctly\n"); - fflush(stderr); - - /* Test: Thread fiber operations */ - fprintf(stderr, "\n[test] thread fiber operations\n"); - fflush(stderr); - - /* Get stack size of thread fiber (should be 0) */ - size_t thread_stack_size = yafl_fiber_get_stack_size(thread_fiber); - assert(thread_stack_size == 0); - fprintf(stderr, "[test] thread fiber stack size: 0\n"); - fflush(stderr); - - /* Try to destroy thread fiber (should fail) */ - destroy_result = yafl_fiber_destroy(thread_fiber); - assert(destroy_result == NULL); - assert(yafl_fiber_is_thread_converted()); /* Still converted */ - fprintf(stderr, "[test] cannot destroy thread fiber via yafl_fiber_destroy()\n"); - fflush(stderr); - - /* Try to fill watermark on thread fiber (should fail) */ - filled = yafl_fiber_fill_watermark(thread_fiber); - assert(!filled); - fprintf(stderr, "[test] cannot fill watermark on thread fiber\n"); - fflush(stderr); - - /* Test: Default stack size for malloc */ - fprintf(stderr, "\n[test] malloc default stack size\n"); - fflush(stderr); - yafl_fiber_t *fiber_malloc_default = yafl_fiber_create_malloc(0, simple_fiber); - assert(fiber_malloc_default != NULL); - size_t malloc_default_size = yafl_fiber_get_stack_size(fiber_malloc_default); - fprintf(stderr, "[test] malloc default stack size: %zu bytes\n", malloc_default_size); - fflush(stderr); - assert(malloc_default_size > 0); - yafl_fiber_switch(fiber_malloc_default, NULL); - yafl_fiber_destroy(fiber_malloc_default); - - /* Test: Both allocation types */ - fprintf(stderr, "\n[test] both allocation types\n"); - fflush(stderr); - yafl_fiber_t *fiber_vmem = yafl_fiber_create_vmem(16 * 1024, simple_fiber); - yafl_fiber_t *fiber_malloc = yafl_fiber_create_malloc(16 * 1024, simple_fiber); - assert(fiber_vmem != NULL); - assert(fiber_malloc != NULL); - yafl_fiber_switch(fiber_vmem, NULL); - yafl_fiber_switch(fiber_malloc, NULL); - yafl_fiber_destroy(fiber_vmem); - yafl_fiber_destroy(fiber_malloc); - fprintf(stderr, "[test] both vmem and malloc work\n"); - fflush(stderr); - - /* Test: Small stack size */ - fprintf(stderr, "\n[test] small stack size (2KB)\n"); - fflush(stderr); - yafl_fiber_t *fiber_small = yafl_fiber_create_malloc(2 * 1024, simple_fiber); - if(fiber_small != NULL) { - yafl_fiber_switch(fiber_small, NULL); - yafl_fiber_destroy(fiber_small); - fprintf(stderr, "[test] small stack works\n"); - fflush(stderr); - } else { - fprintf(stderr, "[test] small stack allocation failed (acceptable)\n"); - fflush(stderr); - } - - /* Test: Multiple consecutive yields */ - fprintf(stderr, "\n[test] multiple consecutive yields\n"); - fflush(stderr); - yafl_fiber_t *fiber_multi = yafl_fiber_create_vmem(16 * 1024, multi_yield_fiber); - assert(fiber_multi != NULL); - - for(int i = 0; i < 5; i++) { - result = yafl_fiber_switch(fiber_multi, NULL); - assert(result == (void *)(uintptr_t)(0x100 + i)); - fprintf(stderr, "[test] received yield %d: %p\n", i, result); - fflush(stderr); - } - - result = yafl_fiber_switch(fiber_multi, NULL); - assert(result == (void *)0x999); - fprintf(stderr, "[test] fiber finished after 5 yields\n"); - fflush(stderr); - yafl_fiber_destroy(fiber_multi); - - /* Test: Watermark on SUSPENDED fiber */ - fprintf(stderr, "\n[test] watermark on suspended fiber\n"); - fflush(stderr); - yafl_fiber_t *fiber_wm_susp = yafl_fiber_create_vmem(16 * 1024, multi_yield_fiber); - assert(fiber_wm_susp != NULL); - yafl_fiber_fill_watermark(fiber_wm_susp); - - /* Run once and suspend */ - yafl_fiber_switch(fiber_wm_susp, NULL); - assert(yafl_fiber_get_state(fiber_wm_susp) == FCONTEXT_FIBER_SUSPENDED); - - /* Get stack usage while suspended */ - usage = yafl_fiber_get_stack_usage(fiber_wm_susp); - assert(usage != SIZE_MAX && usage > 0); - fprintf(stderr, "[test] stack usage while SUSPENDED: %zu bytes\n", usage); - fflush(stderr); - - /* Finish the fiber */ - while(yafl_fiber_get_state(fiber_wm_susp) != FCONTEXT_FIBER_FINISHED) { - yafl_fiber_switch(fiber_wm_susp, NULL); - } - yafl_fiber_destroy(fiber_wm_susp); - - /* Cleanup */ - yafl_fiber_destroy_thread_fiber(); - - /* Test: Yield from non-fiber context (must be last, after thread fiber destroyed) */ - fprintf(stderr, "\n[test] error: yield from non-fiber context\n"); - fflush(stderr); - result = yafl_fiber_yield((void *)0xBAD); - assert(result == NULL); - fprintf(stderr, "[test] yield from non-fiber context returned NULL\n"); - fflush(stderr); - - fprintf(stderr, "\nPASS: All API functions and error conditions tested\n"); - fflush(stderr); - return 0; -} diff --git a/tests/test_yafl_symmetric.c b/tests/test_yafl_symmetric.c deleted file mode 100644 index 85ea7bb..0000000 --- a/tests/test_yafl_symmetric.c +++ /dev/null @@ -1,211 +0,0 @@ -/** - * test_yafl_symmetric.c - * Symmetric coroutine switching test - * - * Verifies that two fibers can switch back and forth to each other - * with interleaved execution. Tests that context switches occur at least - * twice with proper call ordering. - * - * Copyright Kyle Hayes (2026) - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ - -#include "yafl.h" -#include -#include -#include -#include - -/* Track execution order to verify interleaving */ -#define MAX_EVENTS 16 -static size_t event_count = 0; -static uint32_t events[MAX_EVENTS]; - -/* Event identifiers */ -#define EVENT_MAIN_START 1 -#define EVENT_MAIN_TO_A 2 -#define EVENT_A_ENTER 3 -#define EVENT_A_TO_B 4 -#define EVENT_B_ENTER 5 -#define EVENT_B_TO_A 6 -#define EVENT_A_RESUME 7 -#define EVENT_A_TO_B_AGAIN 8 -#define EVENT_B_RESUME 9 -#define EVENT_B_TO_A_AGAIN 10 -#define EVENT_A_DONE 11 -#define EVENT_MAIN_END 12 - -/* Fiber handle for symmetric switching */ -static yafl_fiber_t *fiber_b = NULL; - -static void record_event(uint32_t event) { - if(event_count < MAX_EVENTS) { events[event_count++] = event; } - fprintf(stderr, " [event] #%zu: %u\n", event_count, event); - fflush(stderr); -} - -/** - * Fiber B - called by fiber A - */ -void *fiber_b_entry(void *initial_data) { - fprintf(stderr, "[fiber_b] entered with data: %p\n", initial_data); - fflush(stderr); - record_event(EVENT_B_ENTER); - - /* Yield back to caller (fiber A) */ - fprintf(stderr, "[fiber_b] yielding back to A\n"); - fflush(stderr); - record_event(EVENT_B_TO_A); - void *resumed_data = yafl_fiber_yield((void *)(uintptr_t)0xB0B0B0B0); - - fprintf(stderr, "[fiber_b] resumed from A with data: %p\n", resumed_data); - fflush(stderr); - assert(resumed_data == (void *)(uintptr_t)0xA1A1A1A1); - record_event(EVENT_B_RESUME); - - /* Yield again */ - fprintf(stderr, "[fiber_b] yielding back to A again\n"); - fflush(stderr); - record_event(EVENT_B_TO_A_AGAIN); - yafl_fiber_yield((void *)(uintptr_t)0xB1B1B1B1); - - fprintf(stderr, "[fiber_b] finishing\n"); - fflush(stderr); - return (void *)(uintptr_t)0xBBBBBBBB; -} - -/** - * Fiber A - calls fiber B, then gets called back - */ -void *fiber_a_entry(void *initial_data) { - fprintf(stderr, "[fiber_a] entered with data: %p\n", initial_data); - fflush(stderr); - record_event(EVENT_A_ENTER); - - assert(fiber_b != NULL); - fprintf(stderr, "[fiber_a] have fiber_b: %p\n", (void *)fiber_b); - fflush(stderr); - - /* First switch: call fiber B */ - fprintf(stderr, "[fiber_a] calling fiber B\n"); - fflush(stderr); - record_event(EVENT_A_TO_B); - void *result = yafl_fiber_switch(fiber_b, (void *)(uintptr_t)0xDEADBEEF); - - fprintf(stderr, "[fiber_a] B returned with: %p\n", result); - fflush(stderr); - assert(result == (void *)(uintptr_t)0xB0B0B0B0); - record_event(EVENT_A_RESUME); - - /* Second switch: call fiber B again */ - fprintf(stderr, "[fiber_a] calling fiber B again\n"); - fflush(stderr); - record_event(EVENT_A_TO_B_AGAIN); - result = yafl_fiber_switch(fiber_b, (void *)(uintptr_t)0xA1A1A1A1); - - fprintf(stderr, "[fiber_a] B returned again with: %p\n", result); - fflush(stderr); - assert(result == (void *)(uintptr_t)0xB1B1B1B1); - record_event(EVENT_A_DONE); - - fprintf(stderr, "[fiber_a] finishing\n"); - fflush(stderr); - return (void *)(uintptr_t)0xAAAAAAAA; -} - -int main(void) { - fprintf(stderr, "=== yafl Symmetric Fiber Test ===\n"); - fflush(stderr); - record_event(EVENT_MAIN_START); - - /* Create fibers */ - fprintf(stderr, "[main] creating fiber A\n"); - fflush(stderr); - yafl_fiber_t *fiber_a = yafl_fiber_create_vmem(24 * 1024, fiber_a_entry); - assert(fiber_a != NULL); - - fprintf(stderr, "[main] creating fiber B\n"); - fflush(stderr); - fiber_b = yafl_fiber_create_vmem(24 * 1024, fiber_b_entry); - assert(fiber_b != NULL); - - fprintf(stderr, "[main] entering fiber A\n"); - fflush(stderr); - record_event(EVENT_MAIN_TO_A); - void *result = yafl_fiber_switch(fiber_a, (void *)(uintptr_t)0xCAFEBABE); - - fprintf(stderr, "[main] fiber A finished with result: %p\n", result); - fflush(stderr); - assert(result == (void *)(uintptr_t)0xAAAAAAAA); - - fprintf(stderr, "[main] cleaning up\n"); - fflush(stderr); - yafl_fiber_destroy(fiber_a); - yafl_fiber_destroy(fiber_b); - yafl_fiber_destroy_thread_fiber(); - record_event(EVENT_MAIN_END); - - /* Verify interleaving */ - fprintf(stderr, "\n[main] verifying execution order\n"); - fflush(stderr); - fprintf(stderr, "[main] total events recorded: %zu\n", event_count); - fflush(stderr); - - assert(event_count >= 10); - fprintf(stderr, "[main] event count check passed\n"); - fflush(stderr); - - /* Verify the sequence contains the required interleaving pattern */ - bool found_a_to_b = false; - bool found_b_to_a = false; - bool found_a_to_b_again = false; - bool found_b_to_a_again = false; - - for(size_t i = 0; i < event_count; i++) { - fprintf(stderr, "[main] event[%zu] = %u\n", i, events[i]); - fflush(stderr); - - if(events[i] == EVENT_A_TO_B) { - found_a_to_b = true; - fprintf(stderr, "[main] found A->B switch\n"); - fflush(stderr); - } - if(events[i] == EVENT_B_TO_A && found_a_to_b) { - found_b_to_a = true; - fprintf(stderr, "[main] found B->A switch\n"); - fflush(stderr); - } - if(events[i] == EVENT_A_TO_B_AGAIN && found_b_to_a) { - found_a_to_b_again = true; - fprintf(stderr, "[main] found A->B again switch\n"); - fflush(stderr); - } - if(events[i] == EVENT_B_TO_A_AGAIN && found_a_to_b_again) { - found_b_to_a_again = true; - fprintf(stderr, "[main] found B->A again switch\n"); - fflush(stderr); - } - } - - assert(found_a_to_b); - fprintf(stderr, "[main] assertion passed: A switched to B\n"); - fflush(stderr); - - assert(found_b_to_a); - fprintf(stderr, "[main] assertion passed: B switched to A\n"); - fflush(stderr); - - assert(found_a_to_b_again); - fprintf(stderr, "[main] assertion passed: A switched to B again\n"); - fflush(stderr); - - assert(found_b_to_a_again); - fprintf(stderr, "[main] assertion passed: B switched to A again\n"); - fflush(stderr); - - fprintf(stderr, "\nPASS: Symmetric fiber switching works correctly\n"); - fflush(stderr); - return 0; -} diff --git a/tests/test_yafl_watermark.c b/tests/test_yafl_watermark.c new file mode 100644 index 0000000..85affb3 --- /dev/null +++ b/tests/test_yafl_watermark.c @@ -0,0 +1,122 @@ +/* + * test_yafl_watermark.c - Dedicated stack watermark tests + * + * Verifies watermark tracking for both allocation modes and confirms that + * watermark queries work while a fiber is suspended and after it completes. + */ + +#include +#include +#include +#include +#include + +#include "../include/yafl.h" + +static const char *alloc_name(yafl_stack_flags_t alloc_flag) { return alloc_flag == YAFL_STACK_FLAGS_VMEM ? "mmap" : "malloc"; } + +static void *complete_after_stack_use(void *arg) { + volatile unsigned char stack_buffer[1536]; + + memset((void *)stack_buffer, (int)(uintptr_t)arg, sizeof(stack_buffer)); + return arg; +} + +static void *suspend_after_stack_use(void *arg) { + volatile unsigned char stack_buffer[768]; + + memset((void *)stack_buffer, 0x5A, sizeof(stack_buffer)); + arg = yafl_fiber_suspend(arg); + + memset((void *)stack_buffer, 0xA5, sizeof(stack_buffer)); + return arg; +} + +static void test_watermark_complete(yafl_stack_flags_t alloc_flag, uintptr_t value) { + printf("test_watermark_complete_%s: ", alloc_name(alloc_flag)); + fflush(stdout); + + yafl_fiber_t *fiber = yafl_fiber_create(complete_after_stack_use, 16 * 1024, alloc_flag | YAFL_STACK_FLAGS_WATERMARK); + assert(fiber != NULL); + + void *result = yafl_fiber_resume(fiber, (void *)value); + assert(result == (void *)value); + assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + + size_t usage = yafl_fiber_stack_high_watermark(fiber); + assert(usage > 0); + assert(usage < 16 * 1024); + + yafl_fiber_destroy(fiber); + + printf("PASS\n"); +} + +static void test_watermark_while_suspended(yafl_stack_flags_t alloc_flag, uintptr_t first_value, uintptr_t second_value) { + printf("test_watermark_while_suspended_%s: ", alloc_name(alloc_flag)); + fflush(stdout); + + yafl_fiber_t *fiber = yafl_fiber_create(suspend_after_stack_use, 16 * 1024, alloc_flag | YAFL_STACK_FLAGS_WATERMARK); + assert(fiber != NULL); + + void *result = yafl_fiber_resume(fiber, (void *)first_value); + assert(result == (void *)first_value); + assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + + size_t suspended_usage = yafl_fiber_stack_high_watermark(fiber); + assert(suspended_usage > 0); + assert(suspended_usage < 16 * 1024); + + result = yafl_fiber_resume(fiber, (void *)second_value); + assert(result == (void *)second_value); + assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + + size_t complete_usage = yafl_fiber_stack_high_watermark(fiber); + assert(complete_usage >= suspended_usage); + assert(complete_usage < 16 * 1024); + + yafl_fiber_destroy(fiber); + + printf("PASS\n"); +} + +static void test_watermark_without_flag(yafl_stack_flags_t alloc_flag, uintptr_t value) { + printf("test_watermark_without_flag_%s: ", alloc_name(alloc_flag)); + fflush(stdout); + + yafl_fiber_t *fiber = yafl_fiber_create(complete_after_stack_use, 16 * 1024, alloc_flag); + assert(fiber != NULL); + assert(yafl_fiber_stack_high_watermark(fiber) == 0); + + void *result = yafl_fiber_resume(fiber, (void *)value); + assert(result == (void *)value); + assert(yafl_fiber_stack_high_watermark(fiber) == 0); + + yafl_fiber_destroy(fiber); + + printf("PASS\n"); +} + +static void test_watermark_null_fiber(void) { + printf("test_watermark_null_fiber: "); + fflush(stdout); + + assert(yafl_fiber_stack_high_watermark(NULL) == 0); + + printf("PASS\n"); +} + +int main(void) { + printf("Running YAFL watermark tests...\n"); + + test_watermark_complete(YAFL_STACK_FLAGS_MALLOC, 0x11); + test_watermark_complete(YAFL_STACK_FLAGS_VMEM, 0x22); + test_watermark_while_suspended(YAFL_STACK_FLAGS_MALLOC, 0x33, 0x44); + test_watermark_while_suspended(YAFL_STACK_FLAGS_VMEM, 0x55, 0x66); + test_watermark_without_flag(YAFL_STACK_FLAGS_MALLOC, 0x77); + test_watermark_without_flag(YAFL_STACK_FLAGS_VMEM, 0x88); + test_watermark_null_fiber(); + + printf("\nAll watermark tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/tests/test_yafl_watermark_mmap.c b/tests/test_yafl_watermark_mmap.c deleted file mode 100644 index 733a495..0000000 --- a/tests/test_yafl_watermark_mmap.c +++ /dev/null @@ -1,80 +0,0 @@ -/** - * test_yafl_watermark.c - * Watermark test with both vmem and malloc stacks - * - * Verifies watermark functionality for stack usage tracking. - * - * Copyright Kyle Hayes (2026) - * Distributed under the Boost Software License, Version 1.0. - */ - -#include -#include -#include - -#include "yafl.h" - -void *watermark_fiber(void *data) { - (void)data; - fprintf(stderr, "[fiber] running\n"); - fflush(stderr); - - /* Use some stack space */ - volatile char buf[1024]; - buf[0] = 1; - buf[1023] = 2; - - return (void *)0xCC; -} - -static void test_watermark(const char *name, yafl_fiber_t *fiber) { - fprintf(stderr, "\n[test] %s watermark\n", name); - fflush(stderr); - - /* Fill watermark before first run */ - bool filled = yafl_fiber_fill_watermark(fiber); - assert(filled); - fprintf(stderr, "[test] watermark filled\n"); - fflush(stderr); - - /* Run the fiber */ - void *result = yafl_fiber_switch(fiber, NULL); - assert(result == (void *)0xCC); - fprintf(stderr, "[test] fiber completed\n"); - fflush(stderr); - - /* Check stack usage */ - size_t used = yafl_fiber_get_stack_usage(fiber); - size_t total = yafl_fiber_get_stack_size(fiber); - - fprintf(stderr, "[test] stack used: %zu / %zu bytes (%.1f%%)\n", - used, total, (used * 100.0) / total); - fflush(stderr); - - assert(used > 0); - assert(used < total); - assert(used >= 1024); /* At least our buffer size */ -} - -int main(void) { - fprintf(stderr, "=== yafl Watermark Test ===\n"); - fflush(stderr); - - /* Test with vmem allocation */ - yafl_fiber_t *fiber_vmem = yafl_fiber_create_vmem(24 * 1024, watermark_fiber); - assert(fiber_vmem != NULL); - test_watermark("vmem", fiber_vmem); - yafl_fiber_destroy(fiber_vmem); - - /* Test with malloc allocation */ - yafl_fiber_t *fiber_malloc = yafl_fiber_create_malloc(24 * 1024, watermark_fiber); - assert(fiber_malloc != NULL); - test_watermark("malloc", fiber_malloc); - yafl_fiber_destroy(fiber_malloc); - - yafl_fiber_destroy_thread_fiber(); - - fprintf(stderr, "\nPASS: Watermark test passed for both allocation types\n"); - fflush(stderr); - return 0; -} From aa7952d13dbbbfee715977283e5d63f178bae0e3 Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Tue, 28 Apr 2026 13:46:13 -0700 Subject: [PATCH 05/14] More updates to streamline the assembly and C code. Updates to test coverage. Co-authored-by: Copilot --- docs/coverage.svg | 4 +- docs/coverage/gcov.css | 100 ++- docs/coverage/index-sort-f.html | 50 +- docs/coverage/index-sort-l.html | 50 +- docs/coverage/index.html | 50 +- docs/coverage/src/index.html | 42 +- docs/coverage/src/yafl.c.func-c.html | 99 ++- docs/coverage/src/yafl.c.func.html | 99 ++- docs/coverage/src/yafl.c.gcov.html | 708 ++++++++---------- docs/coverage/tests/index-sort-f.html | 60 +- docs/coverage/tests/index-sort-l.html | 56 +- docs/coverage/tests/index.html | 64 +- .../tests/test_yafl_basic.c.func-c.html | 52 +- .../tests/test_yafl_basic.c.func.html | 52 +- .../tests/test_yafl_basic.c.gcov.html | 324 ++++---- .../tests/test_yafl_many.c.func-c.html | 19 +- .../coverage/tests/test_yafl_many.c.func.html | 19 +- .../coverage/tests/test_yafl_many.c.gcov.html | 168 ++--- .../test_yafl_suspend_resume.c.func-c.html | 46 +- .../test_yafl_suspend_resume.c.func.html | 46 +- .../test_yafl_suspend_resume.c.gcov.html | 234 +++--- docs/streamline-plan.md | 96 +++ scripts/generate_coverage.sh | 2 +- scripts/make_coverage_badge.sh | 0 src/asm/README.md | 99 ++- ...gas.S => make_context_arm_aapcs_elf_gas.S} | 17 +- ...s.S => make_context_arm_aapcs_macho_gas.S} | 13 +- ...s_elf_gas.S => switch_arm_aapcs_elf_gas.S} | 28 +- ...cho_gas.S => switch_arm_aapcs_macho_gas.S} | 24 +- ...s.S => make_context_arm64_aapcs_elf_gas.S} | 12 +- ...S => make_context_arm64_aapcs_macho_gas.S} | 8 +- ...=> make_context_arm64_aapcs_pe_armasm.asm} | 6 +- ...=> make_context_arm64_aapcs_pe_armclang.S} | 12 +- ...elf_gas.S => switch_arm64_aapcs_elf_gas.S} | 24 +- ...o_gas.S => switch_arm64_aapcs_macho_gas.S} | 19 +- ...m.asm => switch_arm64_aapcs_pe_armasm.asm} | 18 +- ...ang.S => switch_arm64_aapcs_pe_armclang.S} | 22 +- ...gas.S => make_context_i386_sysv_elf_gas.S} | 26 +- ...s.S => make_context_i386_sysv_macho_gas.S} | 15 +- ...v_elf_gas.S => switch_i386_sysv_elf_gas.S} | 30 +- ...cho_gas.S => switch_i386_sysv_macho_gas.S} | 25 +- ...> make_context_loongarch64_sysv_elf_gas.S} | 12 +- ...as.S => switch_loongarch64_sysv_elf_gas.S} | 25 +- ...as.S => make_context_mips32_o32_elf_gas.S} | 26 +- ..._elf_gas.S => switch_mips32_o32_elf_gas.S} | 31 +- src/asm/mips64/README.md | 16 +- ...as.S => make_context_mips64_n64_elf_gas.S} | 16 +- ..._elf_gas.S => switch_mips64_n64_elf_gas.S} | 14 +- ...as.S => make_context_ppc32_sysv_elf_gas.S} | 43 +- ..._elf_gas.S => switch_ppc32_sysv_elf_gas.S} | 39 +- ...as.S => make_context_ppc64_sysv_elf_gas.S} | 47 +- ..._elf_gas.S => switch_ppc64_sysv_elf_gas.S} | 82 +- ....S => make_context_riscv64_sysv_elf_gas.S} | 12 +- ...lf_gas.S => switch_riscv64_sysv_elf_gas.S} | 25 +- ...as.S => make_context_s390x_sysv_elf_gas.S} | 18 +- ..._elf_gas.S => switch_s390x_sysv_elf_gas.S} | 52 +- ....S => make_context_sparc64_sysv_elf_gas.S} | 16 +- ...lf_gas.S => switch_sparc64_sysv_elf_gas.S} | 27 +- ..._gas.S => make_context_x86_64_ms_pe_gas.S} | 27 +- ...asm => make_context_x86_64_ms_pe_masm.asm} | 19 +- ...s.S => make_context_x86_64_sysv_elf_gas.S} | 16 +- ...S => make_context_x86_64_sysv_macho_gas.S} | 12 +- ..._ms_pe_gas.S => switch_x86_64_ms_pe_gas.S} | 33 +- ..._masm.asm => switch_x86_64_ms_pe_masm.asm} | 27 +- ...elf_gas.S => switch_x86_64_sysv_elf_gas.S} | 39 +- ...o_gas.S => switch_x86_64_sysv_macho_gas.S} | 23 +- src/yafl.c | 174 ++--- toolchains/aarch64-apple-darwin.cmake | 4 +- toolchains/aarch64-apple-ios.cmake | 4 +- toolchains/aarch64-pc-linux-gnu.cmake | 4 +- toolchains/aarch64-pc-windows-gnu.cmake | 4 +- toolchains/aarch64-pc-windows-msvc.cmake | 4 +- toolchains/arm-unknown-linux-gnueabihf.cmake | 4 +- toolchains/arm-unknown-linux-musleabihf.cmake | 4 +- toolchains/i386-unknown-linux-gnu.cmake | 4 +- .../mips64el-unknown-linux-gnuabi64.cmake | 4 +- toolchains/mipsel-unknown-linux-gnu.cmake | 4 +- toolchains/powerpc-unknown-linux-gnu.cmake | 4 +- .../powerpc64le-unknown-linux-gnu.cmake | 4 +- .../powerpc64le-unknown-linux-musl.cmake | 4 +- toolchains/riscv64-unknown-linux-gnu.cmake | 4 +- toolchains/riscv64-unknown-linux-musl.cmake | 4 +- toolchains/s390x-ibm-linux-gnu.cmake | 4 +- toolchains/s390x-ibm-linux-musl.cmake | 4 +- toolchains/sparc64-unknown-linux-gnu.cmake | 4 +- toolchains/x86_64-apple-darwin.cmake | 4 +- toolchains/x86_64-pc-linux-gnu.cmake | 4 +- toolchains/x86_64-pc-windows-gnu.cmake | 4 +- toolchains/x86_64-pc-windows-msvc.cmake | 4 +- toolchains/x86_64-unknown-linux-android.cmake | 4 +- 90 files changed, 1926 insertions(+), 1875 deletions(-) create mode 100644 docs/streamline-plan.md mode change 100644 => 100755 scripts/make_coverage_badge.sh rename src/asm/arm/{make_arm_aapcs_elf_gas.S => make_context_arm_aapcs_elf_gas.S} (90%) rename src/asm/arm/{make_arm_aapcs_macho_gas.S => make_context_arm_aapcs_macho_gas.S} (91%) rename src/asm/arm/{jump_arm_aapcs_elf_gas.S => switch_arm_aapcs_elf_gas.S} (84%) rename src/asm/arm/{jump_arm_aapcs_macho_gas.S => switch_arm_aapcs_macho_gas.S} (86%) rename src/asm/arm64/{make_arm64_aapcs_elf_gas.S => make_context_arm64_aapcs_elf_gas.S} (95%) rename src/asm/arm64/{make_arm64_aapcs_macho_gas.S => make_context_arm64_aapcs_macho_gas.S} (95%) rename src/asm/arm64/{make_arm64_aapcs_pe_armasm.asm => make_context_arm64_aapcs_pe_armasm.asm} (97%) rename src/asm/arm64/{make_arm64_aapcs_pe_armclang.S => make_context_arm64_aapcs_pe_armclang.S} (95%) rename src/asm/arm64/{jump_arm64_aapcs_elf_gas.S => switch_arm64_aapcs_elf_gas.S} (92%) rename src/asm/arm64/{jump_arm64_aapcs_macho_gas.S => switch_arm64_aapcs_macho_gas.S} (92%) rename src/asm/arm64/{jump_arm64_aapcs_pe_armasm.asm => switch_arm64_aapcs_pe_armasm.asm} (94%) rename src/asm/arm64/{jump_arm64_aapcs_pe_armclang.S => switch_arm64_aapcs_pe_armclang.S} (92%) rename src/asm/i386/{make_i386_sysv_elf_gas.S => make_context_i386_sysv_elf_gas.S} (87%) rename src/asm/i386/{make_i386_sysv_macho_gas.S => make_context_i386_sysv_macho_gas.S} (90%) rename src/asm/i386/{jump_i386_sysv_elf_gas.S => switch_i386_sysv_elf_gas.S} (86%) rename src/asm/i386/{jump_i386_sysv_macho_gas.S => switch_i386_sysv_macho_gas.S} (86%) rename src/asm/loongarch64/{make_loongarch64_sysv_elf_gas.S => make_context_loongarch64_sysv_elf_gas.S} (92%) rename src/asm/loongarch64/{jump_loongarch64_sysv_elf_gas.S => switch_loongarch64_sysv_elf_gas.S} (89%) rename src/asm/mips/{make_mips32_o32_elf_gas.S => make_context_mips32_o32_elf_gas.S} (85%) rename src/asm/mips/{jump_mips32_o32_elf_gas.S => switch_mips32_o32_elf_gas.S} (84%) rename src/asm/mips64/{make_mips64_n64_elf_gas.S => make_context_mips64_n64_elf_gas.S} (94%) rename src/asm/mips64/{jump_mips64_n64_elf_gas.S => switch_mips64_n64_elf_gas.S} (96%) rename src/asm/ppc32/{make_ppc32_sysv_elf_gas.S => make_context_ppc32_sysv_elf_gas.S} (81%) rename src/asm/ppc32/{jump_ppc32_sysv_elf_gas.S => switch_ppc32_sysv_elf_gas.S} (89%) rename src/asm/ppc64/{make_ppc64_sysv_elf_gas.S => make_context_ppc64_sysv_elf_gas.S} (86%) rename src/asm/ppc64/{jump_ppc64_sysv_elf_gas.S => switch_ppc64_sysv_elf_gas.S} (81%) rename src/asm/riscv64/{make_riscv64_sysv_elf_gas.S => make_context_riscv64_sysv_elf_gas.S} (94%) rename src/asm/riscv64/{jump_riscv64_sysv_elf_gas.S => switch_riscv64_sysv_elf_gas.S} (92%) rename src/asm/s390x/{make_s390x_sysv_elf_gas.S => make_context_s390x_sysv_elf_gas.S} (90%) rename src/asm/s390x/{jump_s390x_sysv_elf_gas.S => switch_s390x_sysv_elf_gas.S} (78%) rename src/asm/sparc64/{make_sparc64_sysv_elf_gas.S => make_context_sparc64_sysv_elf_gas.S} (81%) rename src/asm/sparc64/{jump_sparc64_sysv_elf_gas.S => switch_sparc64_sysv_elf_gas.S} (65%) rename src/asm/x86_64/{make_x86_64_ms_pe_gas.S => make_context_x86_64_ms_pe_gas.S} (93%) rename src/asm/x86_64/{make_x86_64_ms_pe_masm.asm => make_context_x86_64_ms_pe_masm.asm} (94%) rename src/asm/x86_64/{make_x86_64_sysv_elf_gas.S => make_context_x86_64_sysv_elf_gas.S} (94%) rename src/asm/x86_64/{make_x86_64_sysv_macho_gas.S => make_context_x86_64_sysv_macho_gas.S} (91%) rename src/asm/x86_64/{jump_x86_64_ms_pe_gas.S => switch_x86_64_ms_pe_gas.S} (83%) rename src/asm/x86_64/{jump_x86_64_ms_pe_masm.asm => switch_x86_64_ms_pe_masm.asm} (94%) rename src/asm/x86_64/{jump_x86_64_sysv_elf_gas.S => switch_x86_64_sysv_elf_gas.S} (87%) rename src/asm/x86_64/{jump_x86_64_sysv_macho_gas.S => switch_x86_64_sysv_macho_gas.S} (87%) diff --git a/docs/coverage.svg b/docs/coverage.svg index f737a95..0d7ba5a 100644 --- a/docs/coverage.svg +++ b/docs/coverage.svg @@ -8,13 +8,13 @@ - + coverage - 86.1% + 97.3% diff --git a/docs/coverage/gcov.css b/docs/coverage/gcov.css index f329042..1cacc83 100644 --- a/docs/coverage/gcov.css +++ b/docs/coverage/gcov.css @@ -36,6 +36,20 @@ td.title font-style: italic; font-weight: bold; } +/* table footnote */ +td.footnote +{ + text-align: left; + padding-left: 100px; + padding-right: 10px; + background-color: #dae7fe; /* light blue table background color */ + /* dark blue table header color + background-color: #6688d4; */ + white-space: nowrap; + font-family: sans-serif; + font-style: italic; + font-size:70%; +} /* "Line coverage date bins" leader */ td.subTableHeader { @@ -187,6 +201,17 @@ td.coverFile font-family: monospace; } +/* Directory view/File view (all): directory name entry format */ +td.coverDirectory +{ + text-align: left; + padding-left: 10px; + padding-right: 20px; + color: #284fa8; + background-color: #b8d0ff; + font-family: monospace; +} + /* Directory view/File view (all): filename entry format */ td.overallOwner { @@ -528,12 +553,29 @@ pre.source margin-top: 2px; } +/* elided/removed code */ +span.elidedSource +{ + font-family: sans-serif; + /*font-size: 8pt; */ + font-style: italic; + background-color: lightgrey; +} + /* Source code view: line number format */ span.lineNum { background-color: #efe383; } +/* Source code view: line number format when there are deleted + lines in the corresponding location */ +span.lineNumWithDelete +{ + foreground-color: #efe383; + background-color: lightgrey; +} + /* Source code view: format for Cov legend */ span.coverLegendCov { @@ -634,7 +676,17 @@ a.branchTla:visited color: #000000; } -/* Source code view/table entry backround: format for lines classified as "Uncovered New Code (+ => 0): +a.mcdcTla:link +{ + color: #000000; +} + +a.mcdcTla:visited +{ + color: #000000; +} + +/* Source code view/table entry background: format for lines classified as "Uncovered New Code (+ => 0): Newly added code is not tested" */ td.tlaUNC { @@ -645,7 +697,7 @@ td.tlaBgUNC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Uncovered New Code (+ => 0): +/* Source code view/table entry background: format for lines classified as "Uncovered New Code (+ => 0): Newly added code is not tested" */ span.tlaUNC { @@ -670,7 +722,7 @@ td.headerCovTableHeadUNC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Lost Baseline Coverage (1 => 0): +/* Source code view/table entry background: format for lines classified as "Lost Baseline Coverage (1 => 0): Unchanged code is no longer tested" */ td.tlaLBC { @@ -681,7 +733,7 @@ td.tlaBgLBC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Lost Baseline Coverage (1 => 0): +/* Source code view/table entry background: format for lines classified as "Lost Baseline Coverage (1 => 0): Unchanged code is no longer tested" */ span.tlaLBC { @@ -706,7 +758,7 @@ td.headerCovTableHeadLBC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Uncovered Included Code (# => 0): +/* Source code view/table entry background: format for lines classified as "Uncovered Included Code (# => 0): Previously unused code is untested" */ td.tlaUIC { @@ -717,7 +769,7 @@ td.tlaBgUIC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Uncovered Included Code (# => 0): +/* Source code view/table entry background: format for lines classified as "Uncovered Included Code (# => 0): Previously unused code is untested" */ span.tlaUIC { @@ -742,7 +794,7 @@ td.headerCovTableHeadUIC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Uncovered Baseline Code (0 => 0): +/* Source code view/table entry background: format for lines classified as "Uncovered Baseline Code (0 => 0): Unchanged code was untested before, is untested now" */ td.tlaUBC { @@ -753,7 +805,7 @@ td.tlaBgUBC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Uncovered Baseline Code (0 => 0): +/* Source code view/table entry background: format for lines classified as "Uncovered Baseline Code (0 => 0): Unchanged code was untested before, is untested now" */ span.tlaUBC { @@ -778,7 +830,7 @@ td.headerCovTableHeadUBC { background-color: #FF6230; } -/* Source code view/table entry backround: format for lines classified as "Gained Baseline Coverage (0 => 1): +/* Source code view/table entry background: format for lines classified as "Gained Baseline Coverage (0 => 1): Unchanged code is tested now" */ td.tlaGBC { @@ -789,7 +841,7 @@ td.tlaBgGBC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Gained Baseline Coverage (0 => 1): +/* Source code view/table entry background: format for lines classified as "Gained Baseline Coverage (0 => 1): Unchanged code is tested now" */ span.tlaGBC { @@ -814,7 +866,7 @@ td.headerCovTableHeadGBC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Gained Included Coverage (# => 1): +/* Source code view/table entry background: format for lines classified as "Gained Included Coverage (# => 1): Previously unused code is tested now" */ td.tlaGIC { @@ -825,7 +877,7 @@ td.tlaBgGIC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Gained Included Coverage (# => 1): +/* Source code view/table entry background: format for lines classified as "Gained Included Coverage (# => 1): Previously unused code is tested now" */ span.tlaGIC { @@ -850,7 +902,7 @@ td.headerCovTableHeadGIC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Gained New Coverage (+ => 1): +/* Source code view/table entry background: format for lines classified as "Gained New Coverage (+ => 1): Newly added code is tested" */ td.tlaGNC { @@ -861,7 +913,7 @@ td.tlaBgGNC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Gained New Coverage (+ => 1): +/* Source code view/table entry background: format for lines classified as "Gained New Coverage (+ => 1): Newly added code is tested" */ span.tlaGNC { @@ -886,7 +938,7 @@ td.headerCovTableHeadGNC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Covered Baseline Code (1 => 1): +/* Source code view/table entry background: format for lines classified as "Covered Baseline Code (1 => 1): Unchanged code was tested before and is still tested" */ td.tlaCBC { @@ -897,7 +949,7 @@ td.tlaBgCBC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Covered Baseline Code (1 => 1): +/* Source code view/table entry background: format for lines classified as "Covered Baseline Code (1 => 1): Unchanged code was tested before and is still tested" */ span.tlaCBC { @@ -922,7 +974,7 @@ td.headerCovTableHeadCBC { background-color: #CAD7FE; } -/* Source code view/table entry backround: format for lines classified as "Excluded Uncovered Baseline (0 => #): +/* Source code view/table entry background: format for lines classified as "Excluded Uncovered Baseline (0 => #): Previously untested code is unused now" */ td.tlaEUB { @@ -933,7 +985,7 @@ td.tlaBgEUB { background-color: #FFFFFF; } -/* Source code view/table entry backround: format for lines classified as "Excluded Uncovered Baseline (0 => #): +/* Source code view/table entry background: format for lines classified as "Excluded Uncovered Baseline (0 => #): Previously untested code is unused now" */ span.tlaEUB { @@ -958,7 +1010,7 @@ td.headerCovTableHeadEUB { background-color: #FFFFFF; } -/* Source code view/table entry backround: format for lines classified as "Excluded Covered Baseline (1 => #): +/* Source code view/table entry background: format for lines classified as "Excluded Covered Baseline (1 => #): Previously tested code is unused now" */ td.tlaECB { @@ -969,7 +1021,7 @@ td.tlaBgECB { background-color: #FFFFFF; } -/* Source code view/table entry backround: format for lines classified as "Excluded Covered Baseline (1 => #): +/* Source code view/table entry background: format for lines classified as "Excluded Covered Baseline (1 => #): Previously tested code is unused now" */ span.tlaECB { @@ -994,7 +1046,7 @@ td.headerCovTableHeadECB { background-color: #FFFFFF; } -/* Source code view/table entry backround: format for lines classified as "Deleted Uncovered Baseline (0 => -): +/* Source code view/table entry background: format for lines classified as "Deleted Uncovered Baseline (0 => -): Previously untested code has been deleted" */ td.tlaDUB { @@ -1005,7 +1057,7 @@ td.tlaBgDUB { background-color: #FFFFFF; } -/* Source code view/table entry backround: format for lines classified as "Deleted Uncovered Baseline (0 => -): +/* Source code view/table entry background: format for lines classified as "Deleted Uncovered Baseline (0 => -): Previously untested code has been deleted" */ span.tlaDUB { @@ -1030,7 +1082,7 @@ td.headerCovTableHeadDUB { background-color: #FFFFFF; } -/* Source code view/table entry backround: format for lines classified as "Deleted Covered Baseline (1 => -): +/* Source code view/table entry background: format for lines classified as "Deleted Covered Baseline (1 => -): Previously tested code has been deleted" */ td.tlaDCB { @@ -1041,7 +1093,7 @@ td.tlaBgDCB { background-color: #FFFFFF; } -/* Source code view/table entry backround: format for lines classified as "Deleted Covered Baseline (1 => -): +/* Source code view/table entry background: format for lines classified as "Deleted Covered Baseline (1 => -): Previously tested code has been deleted" */ span.tlaDCB { diff --git a/docs/coverage/index-sort-f.html b/docs/coverage/index-sort-f.html index add076a..726501a 100644 --- a/docs/coverage/index-sort-f.html +++ b/docs/coverage/index-sort-f.html @@ -28,21 +28,21 @@ Test: - Generated + yafl Code Coverage Lines: - 86.1 % - 539 - 464 + 97.3 % + 560 + 545 Test Date: - Generated + 2026-04-28 13:42:34 Functions: - 89.5 % - 38 - 34 + 95.7 % + 47 + 45 @@ -80,28 +80,28 @@ Hit - src + src/ -
83.2%83.2%
+
89.2%89.2%
- 83.2 % - 137 - 114 - 87.5 % - 8 - 7 + 89.2 % + 139 + 124 + 84.6 % + 13 + 11 - tests + tests/ -
87.1%87.1%
+
100.0%
- 87.1 % - 402 - 350 - 90.0 % - 30 - 27 + 100.0 % + 421 + 421 + 100.0 % + 34 + 34 @@ -109,7 +109,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/index-sort-l.html b/docs/coverage/index-sort-l.html index ce23a23..a9dada5 100644 --- a/docs/coverage/index-sort-l.html +++ b/docs/coverage/index-sort-l.html @@ -28,21 +28,21 @@ Test: - Generated + yafl Code Coverage Lines: - 86.1 % - 539 - 464 + 97.3 % + 560 + 545 Test Date: - Generated + 2026-04-28 13:42:34 Functions: - 89.5 % - 38 - 34 + 95.7 % + 47 + 45 @@ -80,28 +80,28 @@ Hit - src + src/ -
83.2%83.2%
+
89.2%89.2%
- 83.2 % - 137 - 114 - 87.5 % - 8 - 7 + 89.2 % + 139 + 124 + 84.6 % + 13 + 11 - tests + tests/ -
87.1%87.1%
+
100.0%
- 87.1 % - 402 - 350 - 90.0 % - 30 - 27 + 100.0 % + 421 + 421 + 100.0 % + 34 + 34 @@ -109,7 +109,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/index.html b/docs/coverage/index.html index 8c51e48..406f0e7 100644 --- a/docs/coverage/index.html +++ b/docs/coverage/index.html @@ -28,21 +28,21 @@ Test: - Generated + yafl Code Coverage Lines: - 86.1 % - 539 - 464 + 97.3 % + 560 + 545 Test Date: - Generated + 2026-04-28 13:42:34 Functions: - 89.5 % - 38 - 34 + 95.7 % + 47 + 45 @@ -80,28 +80,28 @@ Hit - src + src/ -
83.2%83.2%
+
89.2%89.2%
- 83.2 % - 137 - 114 - 87.5 % - 8 - 7 + 89.2 % + 139 + 124 + 84.6 % + 13 + 11 - tests + tests/ -
87.1%87.1%
+
100.0%
- 87.1 % - 402 - 350 - 90.0 % - 30 - 27 + 100.0 % + 421 + 421 + 100.0 % + 34 + 34 @@ -109,7 +109,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/src/index.html b/docs/coverage/src/index.html index 0f2c5f4..75aae56 100644 --- a/docs/coverage/src/index.html +++ b/docs/coverage/src/index.html @@ -19,7 +19,7 @@ - + @@ -28,21 +28,21 @@ - + - - - + + + - + - - - + + +
Current view:top level - srctop level - src Coverage
Test:Generatedyafl Code Coverage Lines:83.2 %13711489.2 %139124
Test Date:Generated2026-04-28 13:42:34 Functions:87.5 %8784.6 %1311
@@ -67,9 +67,9 @@ - Filename Sort by file name - Line Coverage Sort by line coverage - Function Coverage Sort by function coverage + File Sort by file name + Line Coverage Sort by line coverage + Function Coverage Sort by function coverage Rate @@ -80,16 +80,16 @@ Hit - yafl.c + yafl.c -
83.2%83.2%
+
89.2%89.2%
- 83.2 % - 137 - 114 - 87.5 % - 8 - 7 + 89.2 % + 139 + 124 + 84.6 % + 13 + 11 @@ -97,7 +97,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/src/yafl.c.func-c.html b/docs/coverage/src/yafl.c.func-c.html index c46b48b..8705079 100644 --- a/docs/coverage/src/yafl.c.func-c.html +++ b/docs/coverage/src/yafl.c.func-c.html @@ -19,7 +19,7 @@ - + @@ -28,21 +28,21 @@ - + - - - + + + - + - - - + + +
Current view:top level - src - yafl.c (source / functions)top level - src - yafl.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines:83.2 %13711489.2 %139124
Test Date:Generated2026-04-28 13:42:34 Functions:87.5 %8784.6 %1311
@@ -61,60 +61,109 @@ Hit count Sort by function hit count + + + + fiber_entry_trampoline + + 0 + + + - yafl_get_page_size + yafl_get_page_size 0 + + + + reinitialize_fiber_context_with_watermark + + 16 + + + + + + yafl_fiber_stack_high_watermark + + 30 + + + + + + get_page_size + + 214 + + + + + + align_stack_pointer + + 244 + + + - yafl_fiber_stack_high_watermark + free_fiber_stack + + 244 - 4 - fiber_entry_trampoline + yafl_fiber_destroy + + 248 - 112 - yafl_fiber_destroy + yafl_fiber_create + + 252 - 118 - yafl_fiber_create + initialize_fiber_context + + 260 - 120 - yafl_fiber_suspend + yafl_fiber_suspend + + 2050 - 1023 - yafl_fiber_resume + yafl_fiber_resume + + 2290 - 1137 - yafl_fiber_status + yafl_fiber_status + + 2864 - 1426 @@ -123,7 +172,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/src/yafl.c.func.html b/docs/coverage/src/yafl.c.func.html index afb0852..3a7a6ba 100644 --- a/docs/coverage/src/yafl.c.func.html +++ b/docs/coverage/src/yafl.c.func.html @@ -19,7 +19,7 @@ - + @@ -28,21 +28,21 @@ - + - - - + + + - + - - - + + +
Current view:top level - src - yafl.c (source / functions)top level - src - yafl.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines:83.2 %13711489.2 %139124
Test Date:Generated2026-04-28 13:42:34 Functions:87.5 %8784.6 %1311
@@ -61,69 +61,118 @@ Hit count Sort by function hit count + + + + align_stack_pointer + + 244 + + + + + + fiber_entry_trampoline + + 0 + + + + + + free_fiber_stack + + 244 + + + - fiber_entry_trampoline + get_page_size + + 214 + + + + + + initialize_fiber_context + + 260 + + + + + + reinitialize_fiber_context_with_watermark + + 16 - 112 - yafl_fiber_create + yafl_fiber_create + + 252 - 120 - yafl_fiber_destroy + yafl_fiber_destroy + + 248 - 118 - yafl_fiber_resume + yafl_fiber_resume + + 2290 - 1137 - yafl_fiber_stack_high_watermark + yafl_fiber_stack_high_watermark + + 30 - 4 - yafl_fiber_status + yafl_fiber_status + + 2864 - 1426 - yafl_fiber_suspend + yafl_fiber_suspend + + 2050 - 1023 - yafl_get_page_size + yafl_get_page_size 0 +
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/src/yafl.c.gcov.html b/docs/coverage/src/yafl.c.gcov.html index e606534..f86cdc2 100644 --- a/docs/coverage/src/yafl.c.gcov.html +++ b/docs/coverage/src/yafl.c.gcov.html @@ -19,7 +19,7 @@ - + @@ -28,21 +28,21 @@ - + - - - + + + - + - - - + + +
Current view:top level - src - yafl.c (source / functions)top level - src - yafl.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines:83.2 %13711489.2 %139124
Test Date:Generated2026-04-28 13:42:34 Functions:87.5 %8784.6 %1311
@@ -95,394 +95,328 @@ 33 : /* Raw context handle - opaque pointer to saved machine state */ 34 : typedef struct yafl_opaque_t *yafl_t; 35 : - 36 : /* Raw transfer between contexts */ - 37 : typedef struct { - 38 : yafl_t prev_context; - 39 : void *data; - 40 : } yafl_transfer_t; - 41 : - 42 : /* Raw entry function type for low-level API */ - 43 : typedef void (*yafl_entry_t)(yafl_transfer_t); - 44 : - 45 : /* Low-level assembly-implemented functions */ - 46 : extern yafl_t make_fcontext(void *sp, size_t size, yafl_entry_t fn); - 47 : extern yafl_transfer_t jump_fcontext(yafl_t const to, void *vp); - 48 : - 49 : /* ======================================================================== - 50 : * Constants and Types - 51 : * ======================================================================== */ - 52 : - 53 : #define FCONTEXT_FIBER_MAGIC 0xF1BE7001 - 54 : #define FCONTEXT_STACK_WATERMARK 0xA5 - 55 : #define FCONTEXT_STACK_ALIGNMENT 16 - 56 : - 57 : typedef enum { - 58 : FCONTEXT_ALLOC_MALLOC, - 59 : FCONTEXT_ALLOC_VMEM - 60 : } yafl_alloc_type_t; - 61 : - 62 : /* Internal fiber structure */ - 63 : struct yafl_fiber { - 64 : uint32_t magic; - 65 : yafl_alloc_type_t alloc_type; - 66 : yafl_fiber_status_t status; - 67 : - 68 : /* Stack management */ - 69 : void *stack_region; - 70 : size_t stack_total_size; - 71 : void *stack_top; - 72 : size_t stack_size; - 73 : bool watermark_filled; - 74 : - 75 : /* Context tracking */ - 76 : yafl_t context; /* Fiber's saved context */ - 77 : yafl_t resumer_context; /* Context to return to */ + 36 : /* Raw entry function type for low-level API */ + 37 : typedef void (*yafl_entry_t)(void *); + 38 : + 39 : /* Low-level assembly-implemented functions */ + 40 : /* Creates the initial saved context for a fiber stack. */ + 41 : extern yafl_t yafl_make_context(void *sp, size_t size, yafl_entry_t fn); + 42 : extern void *yafl_switch(yafl_t *save, yafl_t target, void *data); + 43 : + 44 : /* ======================================================================== + 45 : * Constants and Types + 46 : * ======================================================================== */ + 47 : + 48 : #define FCONTEXT_FIBER_MAGIC 0xF1BE7001 + 49 : #define FCONTEXT_STACK_WATERMARK 0xA5 + 50 : #define FCONTEXT_STACK_ALIGNMENT 16 + 51 : + 52 : typedef enum { FCONTEXT_ALLOC_MALLOC, FCONTEXT_ALLOC_VMEM } yafl_alloc_type_t; + 53 : + 54 : /* Internal fiber structure */ + 55 : struct yafl_fiber { + 56 : uint32_t magic; + 57 : yafl_alloc_type_t alloc_type; + 58 : yafl_fiber_status_t status; + 59 : + 60 : /* Stack management */ + 61 : void *stack_region; + 62 : size_t stack_total_size; + 63 : void *stack_top; + 64 : size_t stack_size; + 65 : bool watermark_filled; + 66 : + 67 : /* Context tracking */ + 68 : yafl_t context; /* Fiber's saved context */ + 69 : yafl_t resumer_context; /* Context to return to */ + 70 : + 71 : /* User entry and result */ + 72 : yafl_fiber_fn user_entry; + 73 : void *cached_result; + 74 : }; + 75 : + 76 : /* Typedef for internal use */ + 77 : typedef struct yafl_fiber yafl_fiber_t; 78 : - 79 : /* User entry and result */ - 80 : yafl_fiber_fn user_entry; - 81 : void *cached_result; - 82 : void *pending_arg; /* Argument for next resume */ - 83 : }; - 84 : - 85 : /* Typedef for internal use */ - 86 : typedef struct yafl_fiber yafl_fiber_t; - 87 : - 88 : /* Thread-local storage */ - 89 : static _Thread_local yafl_fiber_t *tls_current_fiber = NULL; - 90 : - 91 : /* ======================================================================== - 92 : * Utility Functions - 93 : * ======================================================================== */ - 94 : - 95 104 : static size_t get_page_size(void) { - 96 : #ifdef _WIN32 - 97 : SYSTEM_INFO si; - 98 : GetSystemInfo(&si); - 99 : return (size_t)si.dwPageSize; - 100 : #else - 101 208 : long page_size = sysconf(_SC_PAGE_SIZE); - 102 104 : if (page_size <= 0) { - 103 : return 4096; - 104 : } - 105 104 : return (size_t)page_size; - 106 : #endif - 107 : } - 108 : - 109 116 : static void *align_stack_pointer(void *ptr) { - 110 116 : uintptr_t addr = (uintptr_t)ptr; - 111 116 : return (void *)(addr & ~(FCONTEXT_STACK_ALIGNMENT - 1)); - 112 : } - 113 : - 114 : /* ======================================================================== - 115 : * Trampoline: Adapts Low-Level API to High-Level Fiber API - 116 : * ======================================================================== */ - 117 : - 118 : /* - 119 : * This is called as the entry function by make_fcontext(). - 120 : * It wraps the user's entry function, manages state, and handles the result. - 121 : */ - 122 112 : static void fiber_entry_trampoline(yafl_transfer_t t) { - 123 112 : yafl_fiber_t *fiber = (yafl_fiber_t *)t.data; - 124 : - 125 : /* Save resumer's context (who called resume on us) */ - 126 112 : fiber->resumer_context = t.prev_context; - 127 : - 128 : /* Update status and TLS */ - 129 112 : fiber->status = YAFL_FIBER_STATUS_RUNNING; - 130 112 : tls_current_fiber = fiber; - 131 : - 132 : /* Call user entry with argument from first resume */ - 133 112 : void *result = fiber->user_entry(fiber->pending_arg); - 134 : - 135 : /* Mark complete and cache result */ - 136 112 : fiber->status = YAFL_FIBER_STATUS_COMPLETE; - 137 112 : fiber->cached_result = result; - 138 112 : tls_current_fiber = NULL; - 139 : - 140 : /* Return to resumer with final result */ - 141 112 : jump_fcontext(fiber->resumer_context, result); - 142 : - 143 : /* Should never reach here */ - 144 0 : } + 79 : /* Thread-local storage */ + 80 : static _Thread_local yafl_fiber_t *tls_current_fiber = NULL; + 81 : + 82 : static void fiber_entry_trampoline(void *arg); + 83 : + 84 244 : static void free_fiber_stack(yafl_fiber_t *fiber) { + 85 244 : if(fiber == NULL || fiber->stack_region == NULL) { return; } + 86 : + 87 244 : if(fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { + 88 30 : free(fiber->stack_region); + 89 244 : } else if(fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { + 90 : #ifdef _WIN32 + 91 : VirtualFree(fiber->stack_region, 0, MEM_RELEASE); + 92 : #else + 93 214 : munmap(fiber->stack_region, fiber->stack_total_size); + 94 : #endif + 95 214 : } + 96 : + 97 244 : fiber->stack_region = NULL; + 98 244 : fiber->stack_total_size = 0; + 99 244 : fiber->stack_top = NULL; + 100 244 : fiber->stack_size = 0; + 101 244 : } + 102 : + 103 260 : static bool initialize_fiber_context(yafl_fiber_t *fiber) { + 104 260 : fiber->context = yafl_make_context(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); + 105 260 : return fiber->context != NULL; + 106 : } + 107 : + 108 16 : static bool reinitialize_fiber_context_with_watermark(yafl_fiber_t *fiber) { + 109 16 : memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); + 110 16 : return initialize_fiber_context(fiber); + 111 : } + 112 : + 113 : /* ======================================================================== + 114 : * Utility Functions + 115 : * ======================================================================== */ + 116 : + 117 214 : static size_t get_page_size(void) { + 118 : #ifdef _WIN32 + 119 : SYSTEM_INFO si; + 120 : GetSystemInfo(&si); + 121 : return (size_t)si.dwPageSize; + 122 : #else + 123 214 : long page_size = sysconf(_SC_PAGE_SIZE); + 124 214 : if(page_size <= 0) { return 4096; } + 125 214 : return (size_t)page_size; + 126 : #endif + 127 214 : } + 128 : + 129 244 : static void *align_stack_pointer(void *ptr) { + 130 244 : uintptr_t addr = (uintptr_t)ptr; + 131 488 : return (void *)(addr & ~((uintptr_t)FCONTEXT_STACK_ALIGNMENT - 1)); + 132 244 : } + 133 : + 134 : /* ======================================================================== + 135 : * Trampoline: Adapts Low-Level API to High-Level Fiber API + 136 : * ======================================================================== */ + 137 : + 138 : /* + 139 : * This is called as the entry function by yafl_make_context(). + 140 : * It wraps the user's entry function, manages state, and handles the result. + 141 : */ + 142 0 : static void fiber_entry_trampoline(void *arg) { + 143 0 : yafl_fiber_t *fiber = tls_current_fiber; + 144 0 : if(fiber == NULL) { abort(); } 145 : - 146 : /* ======================================================================== - 147 : * Fiber Creation - 148 : * ======================================================================== */ + 146 : /* Update status and TLS */ + 147 0 : fiber->status = YAFL_FIBER_STATUS_RUNNING; + 148 0 : tls_current_fiber = fiber; 149 : - 150 120 : extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, - 151 : yafl_stack_flags_t flags) { - 152 : /* Validate fiber function is not NULL */ - 153 120 : if (fiber_fn == NULL) { - 154 : return NULL; - 155 : } - 156 : - 157 : /* Validate exactly one allocation type is set */ - 158 119 : int alloc_flags = flags & (YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_VMEM); - 159 119 : if (alloc_flags != YAFL_STACK_FLAGS_MALLOC && alloc_flags != YAFL_STACK_FLAGS_VMEM) { - 160 : return NULL; - 161 : } - 162 : - 163 116 : bool use_vmem = (flags & YAFL_STACK_FLAGS_VMEM) != 0; - 164 116 : bool use_watermark = (flags & YAFL_STACK_FLAGS_WATERMARK) != 0; - 165 : - 166 : /* Allocate fiber structure */ - 167 116 : yafl_fiber_t *fiber = malloc(sizeof(yafl_fiber_t)); - 168 116 : if (fiber == NULL) { - 169 : return NULL; - 170 : } - 171 : - 172 : /* Initialize fiber structure */ - 173 116 : fiber->magic = FCONTEXT_FIBER_MAGIC; - 174 116 : fiber->alloc_type = use_vmem ? FCONTEXT_ALLOC_VMEM : FCONTEXT_ALLOC_MALLOC; - 175 116 : fiber->status = YAFL_FIBER_STATUS_SUSPENDED; - 176 116 : fiber->user_entry = fiber_fn; - 177 116 : fiber->cached_result = NULL; - 178 116 : fiber->pending_arg = NULL; - 179 116 : fiber->watermark_filled = use_watermark; - 180 116 : fiber->context = NULL; - 181 116 : fiber->resumer_context = NULL; - 182 : - 183 : /* Use default stack size if not specified */ - 184 116 : if (stack_size == 0) { - 185 1 : stack_size = FCONTEXT_DEFAULT_STACK_SIZE; - 186 : } - 187 : - 188 : /* Allocate stack based on allocation type */ - 189 116 : if (use_vmem) { - 190 104 : size_t page_size = get_page_size(); - 191 104 : size_t stack_with_overhead = stack_size + 256; - 192 104 : size_t aligned_stack_size = ((stack_with_overhead + page_size - 1) / page_size) * page_size; - 193 104 : size_t guard_size = page_size; - 194 104 : size_t total_size = guard_size + aligned_stack_size + guard_size; - 195 : - 196 104 : void *region = NULL; - 197 : #ifdef _WIN32 - 198 : region = VirtualAlloc(NULL, total_size, MEM_RESERVE, PAGE_NOACCESS); - 199 : if (region == NULL) { - 200 : free(fiber); - 201 : return NULL; - 202 : } - 203 : void *stack_base = (char *)region + guard_size; - 204 : if (!VirtualAlloc(stack_base, aligned_stack_size, MEM_COMMIT, PAGE_READWRITE)) { - 205 : VirtualFree(region, 0, MEM_RELEASE); - 206 : free(fiber); - 207 : return NULL; - 208 : } - 209 : #else - 210 104 : region = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - 211 104 : if (region == MAP_FAILED) { - 212 0 : free(fiber); - 213 0 : return NULL; - 214 : } - 215 104 : if (mprotect(region, guard_size, PROT_NONE) == -1) { - 216 0 : munmap(region, total_size); - 217 0 : free(fiber); - 218 0 : return NULL; - 219 : } - 220 104 : if (mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { - 221 0 : munmap(region, total_size); - 222 0 : free(fiber); - 223 0 : return NULL; - 224 : } - 225 104 : void *stack_base = (char *)region + guard_size; - 226 : #endif - 227 : - 228 104 : void *stack_region_end = (char *)stack_base + aligned_stack_size; - 229 104 : void *stack_top = (char *)stack_region_end - 256; - 230 104 : stack_top = align_stack_pointer(stack_top); - 231 104 : size_t actual_stack_size = (char *)stack_top - (char *)stack_base; - 232 : - 233 104 : fiber->stack_region = region; - 234 104 : fiber->stack_total_size = total_size; - 235 104 : fiber->stack_top = stack_top; - 236 104 : fiber->stack_size = actual_stack_size; - 237 : } else { - 238 : /* malloc allocation */ - 239 12 : size_t allocated_size = stack_size + 256; - 240 12 : void *block = malloc(allocated_size); - 241 12 : if (block == NULL) { - 242 0 : free(fiber); - 243 0 : return NULL; - 244 : } + 150 : /* Call user entry with argument from first resume */ + 151 0 : void *result = fiber->user_entry(arg); + 152 : + 153 : /* Mark complete and cache result */ + 154 0 : fiber->status = YAFL_FIBER_STATUS_COMPLETE; + 155 0 : fiber->cached_result = result; + 156 0 : tls_current_fiber = NULL; + 157 : + 158 : /* Return to resumer with final result */ + 159 0 : yafl_switch(&fiber->context, fiber->resumer_context, result); + 160 : + 161 : /* Should never reach here */ + 162 0 : abort(); + 163 : } + 164 : + 165 : /* ======================================================================== + 166 : * Fiber Creation + 167 : * ======================================================================== */ + 168 : + 169 252 : extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, yafl_stack_flags_t flags) { + 170 : /* Validate fiber function is not NULL */ + 171 252 : if(fiber_fn == NULL) { return NULL; } + 172 : + 173 : /* Validate exactly one allocation type is set */ + 174 250 : int alloc_flags = flags & (YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_VMEM); + 175 250 : if(alloc_flags != YAFL_STACK_FLAGS_MALLOC && alloc_flags != YAFL_STACK_FLAGS_VMEM) { return NULL; } + 176 : + 177 244 : bool use_vmem = (flags & YAFL_STACK_FLAGS_VMEM) != 0; + 178 244 : bool use_watermark = (flags & YAFL_STACK_FLAGS_WATERMARK) != 0; + 179 : + 180 : /* Allocate fiber structure */ + 181 244 : yafl_fiber_t *fiber = malloc(sizeof(yafl_fiber_t)); + 182 244 : if(fiber == NULL) { return NULL; } + 183 : + 184 : /* Initialize fiber structure */ + 185 244 : fiber->magic = FCONTEXT_FIBER_MAGIC; + 186 244 : fiber->alloc_type = use_vmem ? FCONTEXT_ALLOC_VMEM : FCONTEXT_ALLOC_MALLOC; + 187 244 : fiber->status = YAFL_FIBER_STATUS_SUSPENDED; + 188 244 : fiber->user_entry = fiber_fn; + 189 244 : fiber->cached_result = NULL; + 190 244 : fiber->watermark_filled = use_watermark; + 191 244 : fiber->context = NULL; + 192 244 : fiber->resumer_context = NULL; + 193 244 : fiber->stack_region = NULL; + 194 244 : fiber->stack_total_size = 0; + 195 244 : fiber->stack_top = NULL; + 196 244 : fiber->stack_size = 0; + 197 : + 198 : /* Use default stack size if not specified */ + 199 244 : if(stack_size == 0) { stack_size = FCONTEXT_DEFAULT_STACK_SIZE; } + 200 : + 201 : /* Allocate stack based on allocation type */ + 202 244 : if(use_vmem) { + 203 214 : size_t page_size = get_page_size(); + 204 214 : size_t stack_with_overhead = stack_size + 256; + 205 214 : size_t aligned_stack_size = ((stack_with_overhead + page_size - 1) / page_size) * page_size; + 206 214 : size_t guard_size = page_size; + 207 214 : size_t total_size = guard_size + aligned_stack_size + guard_size; + 208 : + 209 214 : void *region = NULL; + 210 : #ifdef _WIN32 + 211 : region = VirtualAlloc(NULL, total_size, MEM_RESERVE, PAGE_NOACCESS); + 212 : if(region == NULL) { goto create_fail; } + 213 : fiber->stack_region = region; + 214 : fiber->stack_total_size = total_size; + 215 : void *stack_base = (char *)region + guard_size; + 216 : if(!VirtualAlloc(stack_base, aligned_stack_size, MEM_COMMIT, PAGE_READWRITE)) { goto create_fail; } + 217 : #else + 218 214 : region = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + 219 214 : if(region == MAP_FAILED) { goto create_fail; } + 220 214 : fiber->stack_region = region; + 221 214 : fiber->stack_total_size = total_size; + 222 214 : if(mprotect(region, guard_size, PROT_NONE) == -1) { goto create_fail; } + 223 214 : if(mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { goto create_fail; } + 224 214 : void *stack_base = (char *)region + guard_size; + 225 : #endif + 226 : + 227 214 : void *stack_region_end = (char *)stack_base + aligned_stack_size; + 228 214 : void *stack_top = (char *)stack_region_end - 256; + 229 214 : stack_top = align_stack_pointer(stack_top); + 230 214 : size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)stack_base); + 231 : + 232 214 : fiber->stack_top = stack_top; + 233 214 : fiber->stack_size = actual_stack_size; + 234 214 : } else { + 235 : /* malloc allocation */ + 236 30 : size_t allocated_size = stack_size + 256; + 237 30 : void *block = malloc(allocated_size); + 238 30 : if(block == NULL) { goto create_fail; } + 239 30 : fiber->stack_region = block; + 240 30 : fiber->stack_total_size = allocated_size; + 241 : + 242 30 : void *block_end = (char *)block + allocated_size; + 243 30 : void *stack_top = (char *)block_end - 256; + 244 30 : stack_top = align_stack_pointer(stack_top); 245 : - 246 12 : void *block_end = (char *)block + allocated_size; - 247 12 : void *stack_top = (char *)block_end - 256; - 248 12 : stack_top = align_stack_pointer(stack_top); - 249 : - 250 12 : size_t actual_stack_size = (char *)stack_top - (char *)block; + 246 30 : size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)block); + 247 : + 248 30 : fiber->stack_top = stack_top; + 249 30 : fiber->stack_size = actual_stack_size; + 250 30 : } 251 : - 252 12 : fiber->stack_region = block; - 253 12 : fiber->stack_total_size = allocated_size; - 254 12 : fiber->stack_top = stack_top; - 255 12 : fiber->stack_size = actual_stack_size; - 256 : } - 257 : - 258 : /* Initialize the low-level context with trampoline as entry */ - 259 116 : fiber->context = make_fcontext(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); - 260 116 : if (fiber->context == NULL) { - 261 0 : if (use_vmem) { - 262 : #ifdef _WIN32 - 263 : VirtualFree(fiber->stack_region, 0, MEM_RELEASE); - 264 : #else - 265 0 : munmap(fiber->stack_region, fiber->stack_total_size); - 266 : #endif - 267 : } else { - 268 0 : free(fiber->stack_region); - 269 : } - 270 0 : free(fiber); - 271 0 : return NULL; - 272 : } - 273 : - 274 : /* Apply watermark if requested */ - 275 116 : if (use_watermark) { - 276 4 : memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); - 277 : /* Reinitialize context after watermark (overwrites filled area) */ - 278 4 : fiber->context = make_fcontext(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); - 279 4 : if (fiber->context == NULL) { - 280 0 : if (use_vmem) { - 281 : #ifdef _WIN32 - 282 : VirtualFree(fiber->stack_region, 0, MEM_RELEASE); - 283 : #else - 284 0 : munmap(fiber->stack_region, fiber->stack_total_size); - 285 : #endif - 286 : } else { - 287 0 : free(fiber->stack_region); - 288 : } - 289 0 : free(fiber); - 290 0 : return NULL; - 291 : } - 292 : } - 293 : - 294 : return fiber; - 295 : } - 296 : - 297 : /* ======================================================================== - 298 : * Fiber Control Flow - 299 : * ======================================================================== */ + 252 : /* Initialize the low-level context with trampoline as entry */ + 253 244 : if(!initialize_fiber_context(fiber)) { goto create_fail; } + 254 : + 255 : /* Apply watermark if requested */ + 256 244 : if(use_watermark) { + 257 16 : if(!reinitialize_fiber_context_with_watermark(fiber)) { goto create_fail; } + 258 16 : } + 259 : + 260 244 : return fiber; + 261 : + 262 : create_fail: + 263 0 : free_fiber_stack(fiber); + 264 0 : free(fiber); + 265 0 : return NULL; + 266 252 : } + 267 : + 268 : /* ======================================================================== + 269 : * Fiber Control Flow + 270 : * ======================================================================== */ + 271 : + 272 2290 : extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { + 273 : /* Validation */ + 274 2290 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return NULL; } + 275 : + 276 : /* If complete, return cached result (idempotent) */ + 277 2288 : if(fiber->status == YAFL_FIBER_STATUS_COMPLETE) { return fiber->cached_result; } + 278 : + 279 : /* Cannot resume running fiber */ + 280 2286 : if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return NULL; } + 281 : + 282 : /* Update status and TLS */ + 283 2286 : fiber->status = YAFL_FIBER_STATUS_RUNNING; + 284 2286 : tls_current_fiber = fiber; + 285 : + 286 : /* Perform context switch */ + 287 2286 : void *result = yafl_switch(&fiber->resumer_context, fiber->context, arg); + 288 : + 289 : /* Back in resumer */ + 290 2286 : tls_current_fiber = NULL; + 291 : + 292 2286 : return result; + 293 2290 : } + 294 : + 295 2050 : extern void *yafl_fiber_suspend(void *result) { + 296 2050 : yafl_fiber_t *current = tls_current_fiber; + 297 : + 298 : /* Must be in a fiber */ + 299 2050 : if(current == NULL) { return NULL; } 300 : - 301 1137 : extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { - 302 : /* Validation */ - 303 1137 : if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { - 304 : return NULL; - 305 : } + 301 : /* Update status */ + 302 2050 : current->status = YAFL_FIBER_STATUS_SUSPENDED; + 303 : + 304 : /* Switch back to resumer */ + 305 2050 : void *arg = yafl_switch(&current->context, current->resumer_context, result); 306 : - 307 : /* If complete, return cached result (idempotent) */ - 308 1136 : if (fiber->status == YAFL_FIBER_STATUS_COMPLETE) { - 309 1 : return fiber->cached_result; - 310 : } - 311 : - 312 : /* Cannot resume running fiber */ - 313 1135 : if (fiber->status == YAFL_FIBER_STATUS_RUNNING) { - 314 : return NULL; - 315 : } - 316 : - 317 : /* Store argument for delivery */ - 318 1135 : fiber->pending_arg = arg; - 319 : - 320 : /* Update status and TLS */ - 321 1135 : fiber->status = YAFL_FIBER_STATUS_RUNNING; - 322 1135 : tls_current_fiber = fiber; + 307 : /* When resumed - restore state */ + 308 2050 : current->status = YAFL_FIBER_STATUS_RUNNING; + 309 2050 : tls_current_fiber = current; + 310 : + 311 : /* Return argument passed to resume */ + 312 2050 : return arg; + 313 2050 : } + 314 : + 315 : /* ======================================================================== + 316 : * Fiber Status and Monitoring + 317 : * ======================================================================== */ + 318 : + 319 2864 : extern yafl_fiber_status_t yafl_fiber_status(yafl_fiber_t *fiber) { + 320 2864 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return YAFL_FIBER_STATUS_ERR; } + 321 2862 : return fiber->status; + 322 2864 : } 323 : - 324 : /* Pass fiber pointer to trampoline on first resume */ - 325 1135 : void *transfer_data = (void *)fiber; + 324 30 : extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { + 325 30 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC || !fiber->watermark_filled) { return 0; } 326 : - 327 : /* Perform context switch */ - 328 1135 : yafl_transfer_t t = jump_fcontext(fiber->context, transfer_data); - 329 : - 330 : /* Back in resumer - update fiber's context for next resume */ - 331 1135 : fiber->context = t.prev_context; - 332 1135 : tls_current_fiber = NULL; - 333 : - 334 1135 : return t.data; - 335 : } - 336 : - 337 1023 : extern void *yafl_fiber_suspend(void *result) { - 338 1023 : yafl_fiber_t *current = tls_current_fiber; + 327 : /* Scan from stack base for watermark bytes */ + 328 16 : unsigned char *stack_base = (unsigned char *)fiber->stack_top - fiber->stack_size; + 329 16 : size_t unused = 0; + 330 : + 331 386960 : while(unused < fiber->stack_size && stack_base[unused] == FCONTEXT_STACK_WATERMARK) { unused++; } + 332 : + 333 16 : return fiber->stack_size - unused; + 334 30 : } + 335 : + 336 : /* ======================================================================== + 337 : * Cleanup + 338 : * ======================================================================== */ 339 : - 340 : /* Must be in a fiber */ - 341 1023 : if (current == NULL) { - 342 : return NULL; - 343 : } - 344 : - 345 : /* Update status */ - 346 1023 : current->status = YAFL_FIBER_STATUS_SUSPENDED; + 340 248 : extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { + 341 248 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return; } + 342 : + 343 : /* Cannot destroy running fiber */ + 344 244 : if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return; } + 345 : + 346 244 : free_fiber_stack(fiber); 347 : - 348 : /* Switch back to resumer */ - 349 1023 : yafl_transfer_t t = jump_fcontext(current->resumer_context, result); - 350 : - 351 : /* When resumed - restore state */ - 352 1023 : current->status = YAFL_FIBER_STATUS_RUNNING; - 353 1023 : current->resumer_context = t.prev_context; - 354 1023 : tls_current_fiber = current; - 355 : - 356 : /* Return argument passed to resume */ - 357 1023 : return current->pending_arg; - 358 : } - 359 : - 360 : /* ======================================================================== - 361 : * Fiber Status and Monitoring - 362 : * ======================================================================== */ - 363 : - 364 1426 : extern yafl_fiber_status_t yafl_fiber_status(yafl_fiber_t *fiber) { - 365 1426 : if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { - 366 : return YAFL_FIBER_STATUS_ERR; - 367 : } - 368 1425 : return fiber->status; - 369 : } - 370 : - 371 4 : extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { - 372 4 : if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC || !fiber->watermark_filled) { - 373 : return 0; - 374 : } - 375 : - 376 : /* Scan from stack base for watermark bytes */ - 377 2 : unsigned char *stack_base = (unsigned char *)fiber->stack_top - fiber->stack_size; - 378 2 : size_t unused = 0; - 379 : - 380 36434 : while (unused < fiber->stack_size && stack_base[unused] == FCONTEXT_STACK_WATERMARK) { - 381 36432 : unused++; - 382 : } - 383 : - 384 2 : return fiber->stack_size - unused; - 385 : } - 386 : - 387 : /* ======================================================================== - 388 : * Cleanup - 389 : * ======================================================================== */ - 390 : - 391 118 : extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { - 392 118 : if (fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { - 393 : return; - 394 : } - 395 : - 396 : /* Cannot destroy running fiber */ - 397 116 : if (fiber->status == YAFL_FIBER_STATUS_RUNNING) { - 398 : return; - 399 : } - 400 : - 401 : /* Free stack based on allocation type */ - 402 116 : if (fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { - 403 12 : free(fiber->stack_region); - 404 104 : } else if (fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { - 405 : #ifdef _WIN32 - 406 : VirtualFree(fiber->stack_region, 0, MEM_RELEASE); - 407 : #else - 408 104 : munmap(fiber->stack_region, fiber->stack_total_size); - 409 : #endif - 410 : } - 411 : - 412 : /* Invalidate and free */ - 413 116 : fiber->magic = 0; - 414 116 : free(fiber); - 415 : } - 416 : - 417 : /* ======================================================================== - 418 : * Utilities - 419 : * ======================================================================== */ - 420 : - 421 0 : extern size_t yafl_get_page_size(void) { - 422 0 : return get_page_size(); - 423 : } + 348 : /* Invalidate and free */ + 349 244 : fiber->magic = 0; + 350 244 : free(fiber); + 351 248 : } + 352 : + 353 : /* ======================================================================== + 354 : * Utilities + 355 : * ======================================================================== */ + 356 : + 357 0 : extern size_t yafl_get_page_size(void) { return get_page_size(); } @@ -491,7 +425,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/index-sort-f.html b/docs/coverage/tests/index-sort-f.html index 7e2b0c5..aac9999 100644 --- a/docs/coverage/tests/index-sort-f.html +++ b/docs/coverage/tests/index-sort-f.html @@ -19,7 +19,7 @@ - + @@ -28,21 +28,21 @@ - + - - - + + + - + - - - + + +
Current view:top level - teststop level - tests Coverage
Test:Generatedyafl Code Coverage Lines:87.1 %402350100.0 %421421
Test Date:Generated2026-04-28 13:42:34 Functions:90.0 %3027100.0 %3434
@@ -67,7 +67,7 @@ - Filename Sort by file name + File Sort by file name Line Coverage Sort by line coverage Function Coverage Sort by function coverage @@ -80,49 +80,49 @@ Hit - test_yafl_guard.c + test_yafl_many.c -
32.5%32.5%
+
100.0%
- 32.5 % - 77 - 25 - 25.0 % - 4 - 1 + 100.0 % + 78 + 78 + 100.0 % + 2 + 2 - test_yafl_many.c + test_yafl_watermark.c
100.0%
100.0 % - 65 - 65 + 76 + 76 100.0 % - 2 - 2 + 8 + 8 - test_yafl_suspend_resume.c + test_yafl_suspend_resume.c
100.0%
100.0 % - 107 - 107 + 111 + 111 100.0 % 11 11 - test_yafl_basic.c + test_yafl_basic.c
100.0%
100.0 % - 153 - 153 + 156 + 156 100.0 % 13 13 @@ -133,7 +133,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/index-sort-l.html b/docs/coverage/tests/index-sort-l.html index 2ee6554..85c211f 100644 --- a/docs/coverage/tests/index-sort-l.html +++ b/docs/coverage/tests/index-sort-l.html @@ -19,7 +19,7 @@ - + @@ -28,21 +28,21 @@ - + - - - + + + - + - - - + + +
Current view:top level - teststop level - tests Coverage
Test:Generatedyafl Code Coverage Lines:87.1 %402350100.0 %421421
Test Date:Generated2026-04-28 13:42:34 Functions:90.0 %3027100.0 %3434
@@ -67,7 +67,7 @@ - Filename Sort by file name + File Sort by file name Line Coverage Sort by line coverage Function Coverage Sort by function coverage @@ -80,49 +80,49 @@ Hit - test_yafl_guard.c + test_yafl_watermark.c -
32.5%32.5%
+
100.0%
- 32.5 % - 77 - 25 - 25.0 % - 4 - 1 + 100.0 % + 76 + 76 + 100.0 % + 8 + 8 - test_yafl_many.c + test_yafl_many.c
100.0%
100.0 % - 65 - 65 + 78 + 78 100.0 % 2 2 - test_yafl_suspend_resume.c + test_yafl_suspend_resume.c
100.0%
100.0 % - 107 - 107 + 111 + 111 100.0 % 11 11 - test_yafl_basic.c + test_yafl_basic.c
100.0%
100.0 % - 153 - 153 + 156 + 156 100.0 % 13 13 @@ -133,7 +133,7 @@ - +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/index.html b/docs/coverage/tests/index.html index c06ba1a..e704031 100644 --- a/docs/coverage/tests/index.html +++ b/docs/coverage/tests/index.html @@ -19,7 +19,7 @@ - + @@ -28,21 +28,21 @@ - + - - - + + + - + - - - + + +
Current view:top level - teststop level - tests Coverage
Test:Generatedyafl Code Coverage Lines:87.1 %402350100.0 %421421
Test Date:Generated2026-04-28 13:42:34 Functions:90.0 %3027100.0 %3434
@@ -67,7 +67,7 @@ - Filename Sort by file name + File Sort by file name Line Coverage Sort by line coverage Function Coverage Sort by function coverage @@ -80,60 +80,60 @@ Hit - test_yafl_basic.c + test_yafl_basic.c
100.0%
100.0 % - 153 - 153 + 156 + 156 100.0 % 13 13 - test_yafl_guard.c - -
32.5%32.5%
- - 32.5 % - 77 - 25 - 25.0 % - 4 - 1 - - - test_yafl_many.c + test_yafl_many.c
100.0%
100.0 % - 65 - 65 + 78 + 78 100.0 % 2 2 - test_yafl_suspend_resume.c + test_yafl_suspend_resume.c
100.0%
100.0 % - 107 - 107 + 111 + 111 100.0 % 11 11 + + test_yafl_watermark.c + +
100.0%
+ + 100.0 % + 76 + 76 + 100.0 % + 8 + 8 +
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_basic.c.func-c.html b/docs/coverage/tests/test_yafl_basic.c.func-c.html index ed2a718..c82f80f 100644 --- a/docs/coverage/tests/test_yafl_basic.c.func-c.html +++ b/docs/coverage/tests/test_yafl_basic.c.func-c.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -61,95 +61,109 @@ + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + @@ -158,7 +172,7 @@
Current view:top level - tests - test_yafl_basic.c (source / functions)top level - tests - test_yafl_basic.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %153153156156
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %Hit count Sort by function hit count
cycling_fiber_func12
main12
test_default_stack_size12
test_flag_combinations12
test_null_entry_function12
test_null_fiber_pointer12
test_simple_execution12
test_status_query12
test_suspend_resume_cycles12
test_watermark12
test_watermark_without_flag12
watermarked_fiber_func24
simple_fiber_func48
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_basic.c.func.html b/docs/coverage/tests/test_yafl_basic.c.func.html index d1af4b8..64e1b21 100644 --- a/docs/coverage/tests/test_yafl_basic.c.func.html +++ b/docs/coverage/tests/test_yafl_basic.c.func.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -61,95 +61,109 @@ + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + @@ -158,7 +172,7 @@
Current view:top level - tests - test_yafl_basic.c (source / functions)top level - tests - test_yafl_basic.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %153153156156
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %Hit count Sort by function hit count
cycling_fiber_func12
main12
simple_fiber_func48
test_default_stack_size12
test_flag_combinations12
test_null_entry_function12
test_null_fiber_pointer12
test_simple_execution12
test_status_query12
test_suspend_resume_cycles12
test_watermark12
test_watermark_without_flag12
watermarked_fiber_func24
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_basic.c.gcov.html b/docs/coverage/tests/test_yafl_basic.c.gcov.html index 52bb335..b102721 100644 --- a/docs/coverage/tests/test_yafl_basic.c.gcov.html +++ b/docs/coverage/tests/test_yafl_basic.c.gcov.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -79,313 +79,313 @@ 17 : * Test: Simple Fiber Execution 18 : * ======================================================================== */ 19 : - 20 4 : static void *simple_fiber_func(void *arg) { - 21 4 : return arg; + 20 8 : static void *simple_fiber_func(void *arg) { + 21 8 : return arg; 22 : } 23 : - 24 1 : static void test_simple_execution(void) { - 25 1 : printf("test_simple_execution: "); - 26 1 : fflush(stdout); + 24 2 : static void test_simple_execution(void) { + 25 2 : printf("test_simple_execution: "); + 26 2 : fflush(stdout); 27 : 28 : /* Create fiber with malloc allocation */ - 29 1 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 29 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, 30 : YAFL_STACK_FLAGS_MALLOC); - 31 1 : assert(fiber != NULL); - 32 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 31 2 : assert(fiber != NULL); + 32 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 33 : 34 : /* Resume fiber */ - 35 1 : void *result = yafl_fiber_resume(fiber, (void *)0x1234); - 36 1 : assert(result == (void *)0x1234); - 37 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 35 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1234); + 36 2 : assert(result == (void *)0x1234); + 37 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 38 : 39 : /* Resuming completed fiber returns cached result */ - 40 1 : void *result2 = yafl_fiber_resume(fiber, (void *)0x5678); - 41 1 : assert(result2 == (void *)0x1234); + 40 2 : void *result2 = yafl_fiber_resume(fiber, (void *)0x5678); + 41 2 : assert(result2 == (void *)0x1234); 42 : 43 : /* Cleanup */ - 44 1 : yafl_fiber_destroy(fiber); - 45 1 : yafl_fiber_destroy(NULL); /* Safe to call on NULL */ + 44 2 : yafl_fiber_destroy(fiber); + 45 2 : yafl_fiber_destroy(NULL); /* Safe to call on NULL */ 46 : - 47 1 : printf("PASS\n"); - 48 1 : } + 47 2 : printf("PASS\n"); + 48 2 : } 49 : 50 : /* ======================================================================== 51 : * Test: Flag Combinations 52 : * ======================================================================== */ 53 : - 54 1 : static void test_flag_combinations(void) { - 55 1 : printf("test_flag_combinations: "); - 56 1 : fflush(stdout); + 54 2 : static void test_flag_combinations(void) { + 55 2 : printf("test_flag_combinations: "); + 56 2 : fflush(stdout); 57 : 58 : /* Valid: MALLOC alone */ - 59 1 : yafl_fiber_t *f1 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 59 2 : yafl_fiber_t *f1 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 60 : YAFL_STACK_FLAGS_MALLOC); - 61 1 : assert(f1 != NULL); - 62 1 : yafl_fiber_destroy(f1); + 61 2 : assert(f1 != NULL); + 62 2 : yafl_fiber_destroy(f1); 63 : 64 : /* Valid: VMEM alone */ - 65 1 : yafl_fiber_t *f2 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 65 2 : yafl_fiber_t *f2 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 66 : YAFL_STACK_FLAGS_VMEM); - 67 1 : assert(f2 != NULL); - 68 1 : yafl_fiber_destroy(f2); + 67 2 : assert(f2 != NULL); + 68 2 : yafl_fiber_destroy(f2); 69 : 70 : /* Valid: MALLOC with WATERMARK */ - 71 1 : yafl_fiber_t *f3 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 71 2 : yafl_fiber_t *f3 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 72 : YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_WATERMARK); - 73 1 : assert(f3 != NULL); - 74 1 : yafl_fiber_destroy(f3); + 73 2 : assert(f3 != NULL); + 74 2 : yafl_fiber_destroy(f3); 75 : 76 : /* Valid: VMEM with WATERMARK */ - 77 1 : yafl_fiber_t *f4 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 77 2 : yafl_fiber_t *f4 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 78 : YAFL_STACK_FLAGS_VMEM | YAFL_STACK_FLAGS_WATERMARK); - 79 1 : assert(f4 != NULL); - 80 1 : yafl_fiber_destroy(f4); + 79 2 : assert(f4 != NULL); + 80 2 : yafl_fiber_destroy(f4); 81 : 82 : /* Invalid: NONE */ - 83 1 : yafl_fiber_t *f5 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 83 2 : yafl_fiber_t *f5 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 84 : YAFL_STACK_FLAGS_NONE); - 85 1 : assert(f5 == NULL); + 85 2 : assert(f5 == NULL); 86 : 87 : /* Invalid: MALLOC | VMEM both set */ - 88 1 : yafl_fiber_t *f6 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 88 2 : yafl_fiber_t *f6 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 89 : YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_VMEM); - 90 1 : assert(f6 == NULL); + 90 2 : assert(f6 == NULL); 91 : 92 : /* Invalid: Neither allocation type */ - 93 1 : yafl_fiber_t *f7 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 93 2 : yafl_fiber_t *f7 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 94 : YAFL_STACK_FLAGS_WATERMARK); - 95 1 : assert(f7 == NULL); + 95 2 : assert(f7 == NULL); 96 : - 97 1 : printf("PASS\n"); - 98 1 : } + 97 2 : printf("PASS\n"); + 98 2 : } 99 : 100 : /* ======================================================================== 101 : * Test: Suspend/Resume Cycles 102 : * ======================================================================== */ 103 : - 104 1 : static void *cycling_fiber_func(void *arg) { - 105 1 : void *result = arg; + 104 2 : static void *cycling_fiber_func(void *arg) { + 105 2 : void *result = arg; 106 : - 107 6 : for (int i = 0; i < 5; i++) { - 108 5 : result = yafl_fiber_suspend(result); - 109 : } + 107 12 : for (int i = 0; i < 5; i++) { + 108 10 : result = yafl_fiber_suspend(result); + 109 10 : } 110 : - 111 1 : return result; - 112 : } + 111 4 : return result; + 112 2 : } 113 : - 114 1 : static void test_suspend_resume_cycles(void) { - 115 1 : printf("test_suspend_resume_cycles: "); - 116 1 : fflush(stdout); + 114 2 : static void test_suspend_resume_cycles(void) { + 115 2 : printf("test_suspend_resume_cycles: "); + 116 2 : fflush(stdout); 117 : - 118 1 : yafl_fiber_t *fiber = yafl_fiber_create(cycling_fiber_func, 16 * 1024, + 118 2 : yafl_fiber_t *fiber = yafl_fiber_create(cycling_fiber_func, 16 * 1024, 119 : YAFL_STACK_FLAGS_MALLOC); - 120 1 : assert(fiber != NULL); + 120 2 : assert(fiber != NULL); 121 : 122 : /* Cycle 1 */ - 123 1 : void *result = yafl_fiber_resume(fiber, (void *)0x1); - 124 1 : assert(result == (void *)0x1); - 125 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 123 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1); + 124 2 : assert(result == (void *)0x1); + 125 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 126 : 127 : /* Cycle 2 */ - 128 1 : result = yafl_fiber_resume(fiber, (void *)0x2); - 129 1 : assert(result == (void *)0x2); - 130 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 128 2 : result = yafl_fiber_resume(fiber, (void *)0x2); + 129 2 : assert(result == (void *)0x2); + 130 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 131 : 132 : /* Cycle 3 */ - 133 1 : result = yafl_fiber_resume(fiber, (void *)0x3); - 134 1 : assert(result == (void *)0x3); + 133 2 : result = yafl_fiber_resume(fiber, (void *)0x3); + 134 2 : assert(result == (void *)0x3); 135 : 136 : /* Cycle 4 */ - 137 1 : result = yafl_fiber_resume(fiber, (void *)0x4); - 138 1 : assert(result == (void *)0x4); + 137 2 : result = yafl_fiber_resume(fiber, (void *)0x4); + 138 2 : assert(result == (void *)0x4); 139 : 140 : /* Cycle 5 */ - 141 1 : result = yafl_fiber_resume(fiber, (void *)0x5); - 142 1 : assert(result == (void *)0x5); + 141 2 : result = yafl_fiber_resume(fiber, (void *)0x5); + 142 2 : assert(result == (void *)0x5); 143 : 144 : /* Final cycle - fiber completes */ - 145 1 : result = yafl_fiber_resume(fiber, (void *)0x6); - 146 1 : assert(result == (void *)0x6); - 147 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 145 2 : result = yafl_fiber_resume(fiber, (void *)0x6); + 146 2 : assert(result == (void *)0x6); + 147 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 148 : - 149 1 : yafl_fiber_destroy(fiber); + 149 2 : yafl_fiber_destroy(fiber); 150 : - 151 1 : printf("PASS\n"); - 152 1 : } + 151 2 : printf("PASS\n"); + 152 2 : } 153 : 154 : /* ======================================================================== 155 : * Test: Status Query 156 : * ======================================================================== */ 157 : - 158 1 : static void test_status_query(void) { - 159 1 : printf("test_status_query: "); - 160 1 : fflush(stdout); + 158 2 : static void test_status_query(void) { + 159 2 : printf("test_status_query: "); + 160 2 : fflush(stdout); 161 : - 162 1 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 162 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, 163 : YAFL_STACK_FLAGS_MALLOC); 164 : 165 : /* Check initial status */ - 166 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 166 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 167 : 168 : /* Check status of NULL */ - 169 1 : assert(yafl_fiber_status(NULL) == YAFL_FIBER_STATUS_ERR); + 169 2 : assert(yafl_fiber_status(NULL) == YAFL_FIBER_STATUS_ERR); 170 : 171 : /* Resume and check completion */ - 172 1 : yafl_fiber_resume(fiber, (void *)0x99); - 173 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 172 2 : yafl_fiber_resume(fiber, (void *)0x99); + 173 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 174 : - 175 1 : yafl_fiber_destroy(fiber); + 175 2 : yafl_fiber_destroy(fiber); 176 : - 177 1 : printf("PASS\n"); - 178 1 : } + 177 2 : printf("PASS\n"); + 178 2 : } 179 : 180 : /* ======================================================================== 181 : * Test: Watermark Measurement 182 : * ======================================================================== */ 183 : - 184 2 : static void *watermarked_fiber_func(void *arg) { + 184 4 : static void *watermarked_fiber_func(void *arg) { 185 : /* Allocate some stack space to trigger watermark usage */ - 186 2 : volatile char stack_buffer[1024]; + 186 4 : volatile char stack_buffer[1024]; 187 : /* Use memset to ensure compiler can't optimize the buffer away */ - 188 2 : memset((char *)stack_buffer, 0xAA, sizeof(stack_buffer)); - 189 2 : return arg; - 190 : } + 188 4 : memset((char *)stack_buffer, 0xAA, sizeof(stack_buffer)); + 189 8 : return arg; + 190 4 : } 191 : - 192 1 : static void test_watermark(void) { - 193 1 : printf("test_watermark_malloc: "); - 194 1 : fflush(stdout); + 192 2 : static void test_watermark(void) { + 193 2 : printf("test_watermark_malloc: "); + 194 2 : fflush(stdout); 195 : 196 : /* Test with malloc + watermark */ - 197 1 : yafl_fiber_t *fiber = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, + 197 2 : yafl_fiber_t *fiber = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, 198 : YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_WATERMARK); - 199 1 : assert(fiber != NULL); + 199 2 : assert(fiber != NULL); 200 : 201 : /* Resume and check watermark */ - 202 1 : yafl_fiber_resume(fiber, NULL); - 203 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 202 2 : yafl_fiber_resume(fiber, NULL); + 203 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 204 : - 205 1 : size_t usage = yafl_fiber_stack_high_watermark(fiber); - 206 1 : assert(usage > 0); /* Should have used some stack */ + 205 2 : size_t usage = yafl_fiber_stack_high_watermark(fiber); + 206 2 : assert(usage > 0); /* Should have used some stack */ 207 : - 208 1 : yafl_fiber_destroy(fiber); + 208 2 : yafl_fiber_destroy(fiber); 209 : - 210 1 : printf("PASS\n"); + 210 2 : printf("PASS\n"); 211 : - 212 1 : printf("test_watermark_vmem: "); - 213 1 : fflush(stdout); + 212 2 : printf("test_watermark_vmem: "); + 213 2 : fflush(stdout); 214 : 215 : /* Test with vmem + watermark */ - 216 1 : yafl_fiber_t *fiber2 = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, + 216 2 : yafl_fiber_t *fiber2 = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, 217 : YAFL_STACK_FLAGS_VMEM | YAFL_STACK_FLAGS_WATERMARK); - 218 1 : assert(fiber2 != NULL); + 218 2 : assert(fiber2 != NULL); 219 : - 220 1 : yafl_fiber_resume(fiber2, NULL); - 221 1 : assert(yafl_fiber_status(fiber2) == YAFL_FIBER_STATUS_COMPLETE); + 220 2 : yafl_fiber_resume(fiber2, NULL); + 221 2 : assert(yafl_fiber_status(fiber2) == YAFL_FIBER_STATUS_COMPLETE); 222 : - 223 1 : size_t usage2 = yafl_fiber_stack_high_watermark(fiber2); - 224 1 : assert(usage2 > 0); + 223 2 : size_t usage2 = yafl_fiber_stack_high_watermark(fiber2); + 224 2 : assert(usage2 > 0); 225 : - 226 1 : yafl_fiber_destroy(fiber2); + 226 2 : yafl_fiber_destroy(fiber2); 227 : - 228 1 : printf("PASS\n"); - 229 1 : } + 228 2 : printf("PASS\n"); + 229 2 : } 230 : 231 : /* ======================================================================== 232 : * Test: Watermark Without Flag 233 : * ======================================================================== */ 234 : - 235 1 : static void test_watermark_without_flag(void) { - 236 1 : printf("test_watermark_without_flag: "); - 237 1 : fflush(stdout); + 235 2 : static void test_watermark_without_flag(void) { + 236 2 : printf("test_watermark_without_flag: "); + 237 2 : fflush(stdout); 238 : - 239 1 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 239 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, 240 : YAFL_STACK_FLAGS_MALLOC); - 241 1 : assert(fiber != NULL); + 241 2 : assert(fiber != NULL); 242 : 243 : /* Without watermark flag, should return 0 */ - 244 1 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); + 244 2 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); 245 : - 246 1 : yafl_fiber_resume(fiber, NULL); + 246 2 : yafl_fiber_resume(fiber, NULL); 247 : 248 : /* Still 0 because no watermark was applied */ - 249 1 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); + 249 2 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); 250 : - 251 1 : yafl_fiber_destroy(fiber); + 251 2 : yafl_fiber_destroy(fiber); 252 : - 253 1 : printf("PASS\n"); - 254 1 : } + 253 2 : printf("PASS\n"); + 254 2 : } 255 : 256 : /* ======================================================================== 257 : * Test: NULL Fiber Pointer 258 : * ======================================================================== */ 259 : - 260 1 : static void test_null_fiber_pointer(void) { - 261 1 : printf("test_null_fiber_pointer: "); - 262 1 : fflush(stdout); + 260 2 : static void test_null_fiber_pointer(void) { + 261 2 : printf("test_null_fiber_pointer: "); + 262 2 : fflush(stdout); 263 : 264 : /* Resume NULL fiber */ - 265 1 : void *result = yafl_fiber_resume(NULL, (void *)0x1); - 266 1 : assert(result == NULL); + 265 2 : void *result = yafl_fiber_resume(NULL, (void *)0x1); + 266 2 : assert(result == NULL); 267 : 268 : /* Destroy NULL fiber (safe no-op) */ - 269 1 : yafl_fiber_destroy(NULL); + 269 2 : yafl_fiber_destroy(NULL); 270 : - 271 1 : printf("PASS\n"); - 272 1 : } + 271 2 : printf("PASS\n"); + 272 2 : } 273 : 274 : /* ======================================================================== 275 : * Test: NULL Entry Function 276 : * ======================================================================== */ 277 : - 278 1 : static void test_null_entry_function(void) { - 279 1 : printf("test_null_entry_function: "); - 280 1 : fflush(stdout); + 278 2 : static void test_null_entry_function(void) { + 279 2 : printf("test_null_entry_function: "); + 280 2 : fflush(stdout); 281 : - 282 1 : yafl_fiber_t *fiber = yafl_fiber_create(NULL, 16 * 1024, + 282 2 : yafl_fiber_t *fiber = yafl_fiber_create(NULL, 16 * 1024, 283 : YAFL_STACK_FLAGS_MALLOC); - 284 1 : assert(fiber == NULL); + 284 2 : assert(fiber == NULL); 285 : - 286 1 : printf("PASS\n"); - 287 1 : } + 286 2 : printf("PASS\n"); + 287 2 : } 288 : 289 : /* ======================================================================== 290 : * Test: Default Stack Size 291 : * ======================================================================== */ 292 : - 293 1 : static void test_default_stack_size(void) { - 294 1 : printf("test_default_stack_size: "); - 295 1 : fflush(stdout); + 293 2 : static void test_default_stack_size(void) { + 294 2 : printf("test_default_stack_size: "); + 295 2 : fflush(stdout); 296 : 297 : /* Create with 0 stack size (uses default) */ - 298 1 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 0, + 298 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 0, 299 : YAFL_STACK_FLAGS_MALLOC); - 300 1 : assert(fiber != NULL); + 300 2 : assert(fiber != NULL); 301 : - 302 1 : yafl_fiber_resume(fiber, (void *)0x42); - 303 1 : yafl_fiber_destroy(fiber); + 302 2 : yafl_fiber_resume(fiber, (void *)0x42); + 303 2 : yafl_fiber_destroy(fiber); 304 : - 305 1 : printf("PASS\n"); - 306 1 : } + 305 2 : printf("PASS\n"); + 306 2 : } 307 : 308 : /* ======================================================================== 309 : * Main 310 : * ======================================================================== */ 311 : - 312 1 : int main(void) { - 313 1 : printf("Running YAFL basic tests...\n"); + 312 2 : int main(void) { + 313 2 : printf("Running YAFL basic tests...\n"); 314 : - 315 1 : test_simple_execution(); - 316 1 : test_flag_combinations(); - 317 1 : test_suspend_resume_cycles(); - 318 1 : test_status_query(); - 319 1 : test_watermark(); - 320 1 : test_watermark_without_flag(); - 321 1 : test_null_fiber_pointer(); - 322 1 : test_null_entry_function(); - 323 1 : test_default_stack_size(); + 315 2 : test_simple_execution(); + 316 2 : test_flag_combinations(); + 317 2 : test_suspend_resume_cycles(); + 318 2 : test_status_query(); + 319 2 : test_watermark(); + 320 2 : test_watermark_without_flag(); + 321 2 : test_null_fiber_pointer(); + 322 2 : test_null_entry_function(); + 323 2 : test_default_stack_size(); 324 : - 325 1 : printf("\nAll tests passed!\n"); - 326 1 : return 0; + 325 2 : printf("\nAll tests passed!\n"); + 326 2 : return 0; 327 : } @@ -395,7 +395,7 @@
Current view:top level - tests - test_yafl_basic.c (source / functions)top level - tests - test_yafl_basic.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %153153156156
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_many.c.func-c.html b/docs/coverage/tests/test_yafl_many.c.func-c.html index 1126f91..c20f298 100644 --- a/docs/coverage/tests/test_yafl_many.c.func-c.html +++ b/docs/coverage/tests/test_yafl_many.c.func-c.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -61,18 +61,21 @@ + - + + - + + @@ -81,7 +84,7 @@
Current view:top level - tests - test_yafl_many.c (source / functions)top level - tests - test_yafl_many.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %65657878
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %Hit count Sort by function hit count
main12
fiber_entry100200
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_many.c.func.html b/docs/coverage/tests/test_yafl_many.c.func.html index b9d9b9e..63271ac 100644 --- a/docs/coverage/tests/test_yafl_many.c.func.html +++ b/docs/coverage/tests/test_yafl_many.c.func.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -61,18 +61,21 @@ + - + + - + + @@ -81,7 +84,7 @@
Current view:top level - tests - test_yafl_many.c (source / functions)top level - tests - test_yafl_many.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %65657878
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %Hit count Sort by function hit count
fiber_entry100200
main12
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_many.c.gcov.html b/docs/coverage/tests/test_yafl_many.c.gcov.html index 931072c..9e89e0c 100644 --- a/docs/coverage/tests/test_yafl_many.c.gcov.html +++ b/docs/coverage/tests/test_yafl_many.c.gcov.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -85,111 +85,111 @@ 23 : static int counters[NUM_FIBERS]; 24 : static yafl_fiber_t *fibers[NUM_FIBERS]; 25 : - 26 100 : static void *fiber_entry(void *data) { - 27 100 : int id = (int)(uintptr_t)data; + 26 200 : static void *fiber_entry(void *data) { + 27 200 : int id = (int)(uintptr_t)data; 28 : - 29 100 : fprintf(stderr, "[fiber %d] entered\n", id); - 30 100 : fflush(stderr); + 29 200 : fprintf(stderr, "[fiber %d] entered\n", id); + 30 200 : fflush(stderr); 31 : - 32 1100 : for (int i = 0; i < ITERATIONS; i++) { - 33 1000 : fprintf(stderr, "[fiber %d] iteration %d/%d\n", id, i + 1, ITERATIONS); - 34 1000 : fflush(stderr); + 32 2200 : for (int i = 0; i < ITERATIONS; i++) { + 33 2000 : fprintf(stderr, "[fiber %d] iteration %d/%d\n", id, i + 1, ITERATIONS); + 34 2000 : fflush(stderr); 35 : - 36 1000 : counters[id]++; + 36 2000 : counters[id]++; 37 : - 38 1000 : fprintf(stderr, "[fiber %d] suspending\n", id); - 39 1000 : fflush(stderr); - 40 1000 : yafl_fiber_suspend(NULL); + 38 2000 : fprintf(stderr, "[fiber %d] suspending\n", id); + 39 2000 : fflush(stderr); + 40 2000 : yafl_fiber_suspend(NULL); 41 : - 42 1000 : fprintf(stderr, "[fiber %d] resumed\n", id); - 43 1000 : fflush(stderr); - 44 : } + 42 2000 : fprintf(stderr, "[fiber %d] resumed\n", id); + 43 2000 : fflush(stderr); + 44 2000 : } 45 : - 46 100 : fprintf(stderr, "[fiber %d] finishing with result %d\n", id, id + 1000); - 47 100 : fflush(stderr); - 48 100 : return (void *)(uintptr_t)(id + 1000); - 49 : } + 46 200 : fprintf(stderr, "[fiber %d] finishing with result %d\n", id, id + 1000); + 47 200 : fflush(stderr); + 48 200 : return (void *)(uintptr_t)(id + 1000); + 49 200 : } 50 : - 51 1 : int main(void) { - 52 1 : fprintf(stderr, "=== yafl Many Fibers Test ===\n"); - 53 1 : fprintf(stderr, "[main] testing with %d fibers, %d iterations each\n", NUM_FIBERS, ITERATIONS); - 54 1 : fflush(stderr); + 51 2 : int main(void) { + 52 2 : fprintf(stderr, "=== yafl Many Fibers Test ===\n"); + 53 2 : fprintf(stderr, "[main] testing with %d fibers, %d iterations each\n", NUM_FIBERS, ITERATIONS); + 54 2 : fflush(stderr); 55 : 56 : /* Create 100 fibers */ - 57 1 : fprintf(stderr, "[main] creating %d fibers\n", NUM_FIBERS); - 58 1 : fflush(stderr); - 59 101 : for (int i = 0; i < NUM_FIBERS; i++) { - 60 100 : counters[i] = 0; - 61 100 : fibers[i] = yafl_fiber_create(fiber_entry, 24 * 1024, YAFL_STACK_FLAGS_VMEM); - 62 100 : assert(fibers[i] != NULL); + 57 2 : fprintf(stderr, "[main] creating %d fibers\n", NUM_FIBERS); + 58 2 : fflush(stderr); + 59 202 : for (int i = 0; i < NUM_FIBERS; i++) { + 60 200 : counters[i] = 0; + 61 200 : fibers[i] = yafl_fiber_create(fiber_entry, 24 * 1024, YAFL_STACK_FLAGS_VMEM); + 62 200 : assert(fibers[i] != NULL); 63 : 64 : /* Test status on created fiber */ - 65 100 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); - 66 100 : assert(status == YAFL_FIBER_STATUS_SUSPENDED); + 65 200 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); + 66 200 : assert(status == YAFL_FIBER_STATUS_SUSPENDED); 67 : - 68 100 : if (i == 0 || i == NUM_FIBERS - 1) { - 69 2 : fprintf(stderr, "[main] fiber %d: created, status=%d\n", i, status); - 70 2 : fflush(stderr); - 71 : } - 72 : } - 73 1 : fprintf(stderr, "[main] all %d fibers created successfully\n", NUM_FIBERS); - 74 1 : fflush(stderr); + 68 200 : if (i == 0 || i == NUM_FIBERS - 1) { + 69 4 : fprintf(stderr, "[main] fiber %d: created, status=%d\n", i, status); + 70 4 : fflush(stderr); + 71 4 : } + 72 200 : } + 73 2 : fprintf(stderr, "[main] all %d fibers created successfully\n", NUM_FIBERS); + 74 2 : fflush(stderr); 75 : 76 : /* Round-robin scheduling until all done */ - 77 1 : fprintf(stderr, "[main] starting round-robin scheduling\n"); - 78 1 : fflush(stderr); + 77 2 : fprintf(stderr, "[main] starting round-robin scheduling\n"); + 78 2 : fflush(stderr); 79 : - 80 1 : int round = 0; - 81 1 : bool all_done = false; - 82 13 : while (!all_done) { - 83 : all_done = true; - 84 : int active_count = 0; + 80 2 : int round = 0; + 81 2 : bool all_done = false; + 82 26 : while (!all_done) { + 83 24 : all_done = true; + 84 24 : int active_count = 0; 85 : - 86 1212 : for (int i = 0; i < NUM_FIBERS; i++) { - 87 1200 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); - 88 1200 : if (status != YAFL_FIBER_STATUS_COMPLETE) { - 89 1100 : yafl_fiber_resume(fibers[i], (void *)(uintptr_t)i); - 90 1100 : all_done = false; - 91 1100 : active_count++; - 92 : } - 93 : } + 86 2424 : for (int i = 0; i < NUM_FIBERS; i++) { + 87 2400 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); + 88 2400 : if (status != YAFL_FIBER_STATUS_COMPLETE) { + 89 2200 : yafl_fiber_resume(fibers[i], (void *)(uintptr_t)i); + 90 2200 : all_done = false; + 91 2200 : active_count++; + 92 2200 : } + 93 2400 : } 94 : - 95 12 : round++; - 96 12 : if (round <= 3 || all_done) { - 97 4 : fprintf(stderr, "[main] round %d: %d fibers still active\n", round, active_count); - 98 4 : fflush(stderr); - 99 : } - 100 : } + 95 24 : round++; + 96 24 : if (round <= 3 || all_done) { + 97 8 : fprintf(stderr, "[main] round %d: %d fibers still active\n", round, active_count); + 98 8 : fflush(stderr); + 99 8 : } + 100 24 : } 101 : - 102 1 : fprintf(stderr, "[main] all fibers finished after %d rounds\n", round); - 103 1 : fflush(stderr); + 102 2 : fprintf(stderr, "[main] all fibers finished after %d rounds\n", round); + 103 2 : fflush(stderr); 104 : 105 : /* Verify all fibers ran exactly ITERATIONS times */ - 106 1 : fprintf(stderr, "[main] verifying all fibers completed %d iterations\n", ITERATIONS); - 107 1 : fflush(stderr); + 106 2 : fprintf(stderr, "[main] verifying all fibers completed %d iterations\n", ITERATIONS); + 107 2 : fflush(stderr); 108 : - 109 101 : for (int i = 0; i < NUM_FIBERS; i++) { - 110 100 : assert(counters[i] == ITERATIONS); + 109 202 : for (int i = 0; i < NUM_FIBERS; i++) { + 110 200 : assert(counters[i] == ITERATIONS); 111 : - 112 100 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); - 113 100 : assert(status == YAFL_FIBER_STATUS_COMPLETE); + 112 200 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); + 113 200 : assert(status == YAFL_FIBER_STATUS_COMPLETE); 114 : - 115 100 : if (i == 0 || i == NUM_FIBERS - 1) { - 116 2 : fprintf(stderr, "[main] fiber %d: counter=%d, status=%d\n", i, counters[i], status); - 117 2 : fflush(stderr); - 118 : } + 115 200 : if (i == 0 || i == NUM_FIBERS - 1) { + 116 4 : fprintf(stderr, "[main] fiber %d: counter=%d, status=%d\n", i, counters[i], status); + 117 4 : fflush(stderr); + 118 4 : } 119 : - 120 100 : yafl_fiber_destroy(fibers[i]); - 121 : } + 120 200 : yafl_fiber_destroy(fibers[i]); + 121 200 : } 122 : - 123 1 : fprintf(stderr, "[main] all fibers verified and destroyed\n"); - 124 1 : fflush(stderr); + 123 2 : fprintf(stderr, "[main] all fibers verified and destroyed\n"); + 124 2 : fflush(stderr); 125 : - 126 1 : fprintf(stderr, "\nPASS: %d fibers x %d iterations = %d total context switches\n", NUM_FIBERS, + 126 2 : fprintf(stderr, "\nPASS: %d fibers x %d iterations = %d total context switches\n", NUM_FIBERS, 127 : ITERATIONS, NUM_FIBERS * ITERATIONS); - 128 1 : fflush(stderr); - 129 1 : return 0; - 130 : } + 128 2 : fflush(stderr); + 129 2 : return 0; + 130 2 : } @@ -198,7 +198,7 @@
Current view:top level - tests - test_yafl_many.c (source / functions)top level - tests - test_yafl_many.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %65657878
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html b/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html index d40870e..b48bc56 100644 --- a/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html +++ b/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -61,81 +61,93 @@ + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + @@ -144,7 +156,7 @@
Current view:top level - tests - test_yafl_suspend_resume.c (source / functions)top level - tests - test_yafl_suspend_resume.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %107107111111
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %Hit count Sort by function hit count
conditional_suspend_fiber_func12
echo_fiber_func12
main12
multi_suspend_fiber_func12
null_data_fiber_func12
test_bidirectional_data_passing12
test_conditional_suspension12
test_multiple_suspend_points12
test_null_data_passing12
test_vmem_suspend_resume12
vmem_test_func12
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_suspend_resume.c.func.html b/docs/coverage/tests/test_yafl_suspend_resume.c.func.html index 6704222..78788e4 100644 --- a/docs/coverage/tests/test_yafl_suspend_resume.c.func.html +++ b/docs/coverage/tests/test_yafl_suspend_resume.c.func.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -61,81 +61,93 @@ + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + @@ -144,7 +156,7 @@
Current view:top level - tests - test_yafl_suspend_resume.c (source / functions)top level - tests - test_yafl_suspend_resume.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %107107111111
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %Hit count Sort by function hit count
conditional_suspend_fiber_func12
echo_fiber_func12
main12
multi_suspend_fiber_func12
null_data_fiber_func12
test_bidirectional_data_passing12
test_conditional_suspension12
test_multiple_suspend_points12
test_null_data_passing12
test_vmem_suspend_resume12
vmem_test_func12
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html b/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html index a015f7e..ab2951b 100644 --- a/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html +++ b/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - + + - + @@ -78,201 +78,201 @@ 16 : * Test: Bidirectional Data Passing 17 : * ======================================================================== */ 18 : - 19 1 : static void *echo_fiber_func(void *arg) { + 19 2 : static void *echo_fiber_func(void *arg) { 20 : /* Fiber receives initial arg, suspends with it */ - 21 1 : arg = yafl_fiber_suspend(arg); + 21 2 : arg = yafl_fiber_suspend(arg); 22 : 23 : /* Receives new arg, suspends with it */ - 24 1 : arg = yafl_fiber_suspend(arg); + 24 2 : arg = yafl_fiber_suspend(arg); 25 : 26 : /* Receives final arg, returns it */ - 27 1 : return arg; + 27 2 : return arg; 28 : } 29 : - 30 1 : static void test_bidirectional_data_passing(void) { - 31 1 : printf("test_bidirectional_data_passing: "); - 32 1 : fflush(stdout); + 30 2 : static void test_bidirectional_data_passing(void) { + 31 2 : printf("test_bidirectional_data_passing: "); + 32 2 : fflush(stdout); 33 : - 34 1 : yafl_fiber_t *fiber = yafl_fiber_create(echo_fiber_func, 16 * 1024, + 34 2 : yafl_fiber_t *fiber = yafl_fiber_create(echo_fiber_func, 16 * 1024, 35 : YAFL_STACK_FLAGS_MALLOC); - 36 1 : assert(fiber != NULL); + 36 2 : assert(fiber != NULL); 37 : 38 : /* First resume: send 0x1111, receive 0x1111 back */ - 39 1 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); - 40 1 : assert(result == (void *)0x1111); - 41 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 39 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); + 40 2 : assert(result == (void *)0x1111); + 41 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 42 : 43 : /* Second resume: send 0x2222, receive 0x2222 back */ - 44 1 : result = yafl_fiber_resume(fiber, (void *)0x2222); - 45 1 : assert(result == (void *)0x2222); - 46 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 44 2 : result = yafl_fiber_resume(fiber, (void *)0x2222); + 45 2 : assert(result == (void *)0x2222); + 46 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 47 : 48 : /* Third resume: send 0x3333, receive 0x3333 back (fiber completes) */ - 49 1 : result = yafl_fiber_resume(fiber, (void *)0x3333); - 50 1 : assert(result == (void *)0x3333); - 51 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 49 2 : result = yafl_fiber_resume(fiber, (void *)0x3333); + 50 2 : assert(result == (void *)0x3333); + 51 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 52 : - 53 1 : yafl_fiber_destroy(fiber); + 53 2 : yafl_fiber_destroy(fiber); 54 : - 55 1 : printf("PASS\n"); - 56 1 : } + 55 2 : printf("PASS\n"); + 56 2 : } 57 : 58 : /* ======================================================================== 59 : * Test: Multiple Suspend Points 60 : * ======================================================================== */ 61 : - 62 1 : static void *multi_suspend_fiber_func(void *arg) { - 63 11 : for (int i = 0; i < 10; i++) { - 64 10 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x1000 + i)); - 65 : } - 66 1 : return arg; + 62 2 : static void *multi_suspend_fiber_func(void *arg) { + 63 22 : for (int i = 0; i < 10; i++) { + 64 20 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x1000 + i)); + 65 20 : } + 66 2 : return arg; 67 : } 68 : - 69 1 : static void test_multiple_suspend_points(void) { - 70 1 : printf("test_multiple_suspend_points: "); - 71 1 : fflush(stdout); + 69 2 : static void test_multiple_suspend_points(void) { + 70 2 : printf("test_multiple_suspend_points: "); + 71 2 : fflush(stdout); 72 : - 73 1 : yafl_fiber_t *fiber = yafl_fiber_create(multi_suspend_fiber_func, 16 * 1024, + 73 2 : yafl_fiber_t *fiber = yafl_fiber_create(multi_suspend_fiber_func, 16 * 1024, 74 : YAFL_STACK_FLAGS_MALLOC); - 75 1 : assert(fiber != NULL); + 75 2 : assert(fiber != NULL); 76 : - 77 11 : for (int i = 0; i < 10; i++) { - 78 10 : void *result = yafl_fiber_resume(fiber, (void *)(uintptr_t)(0x2000 + i)); - 79 10 : assert(result == (void *)(uintptr_t)(0x1000 + i)); - 80 10 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); - 81 : } + 77 22 : for (int i = 0; i < 10; i++) { + 78 20 : void *result = yafl_fiber_resume(fiber, (void *)(uintptr_t)(0x2000 + i)); + 79 20 : assert(result == (void *)(uintptr_t)(0x1000 + i)); + 80 20 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 81 20 : } 82 : 83 : /* Final resume to complete - should return the last arg passed to resume */ - 84 1 : void *final = yafl_fiber_resume(fiber, (void *)0x3000); - 85 1 : assert(final == (void *)0x3000); - 86 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 84 2 : void *final = yafl_fiber_resume(fiber, (void *)0x3000); + 85 2 : assert(final == (void *)0x3000); + 86 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 87 : - 88 1 : yafl_fiber_destroy(fiber); + 88 2 : yafl_fiber_destroy(fiber); 89 : - 90 1 : printf("PASS\n"); - 91 1 : } + 90 2 : printf("PASS\n"); + 91 2 : } 92 : 93 : /* ======================================================================== 94 : * Test: Conditional Suspension 95 : * ======================================================================== */ 96 : - 97 1 : static void *conditional_suspend_fiber_func(void *arg) { - 98 6 : for (int i = 0; i < 5; i++) { - 99 5 : if (i % 2 == 0) { - 100 3 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x100 + i)); - 101 : } - 102 : } - 103 1 : return arg; + 97 2 : static void *conditional_suspend_fiber_func(void *arg) { + 98 12 : for (int i = 0; i < 5; i++) { + 99 10 : if (i % 2 == 0) { + 100 6 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x100 + i)); + 101 6 : } + 102 10 : } + 103 2 : return arg; 104 : } 105 : - 106 1 : static void test_conditional_suspension(void) { - 107 1 : printf("test_conditional_suspension: "); - 108 1 : fflush(stdout); + 106 2 : static void test_conditional_suspension(void) { + 107 2 : printf("test_conditional_suspension: "); + 108 2 : fflush(stdout); 109 : - 110 1 : yafl_fiber_t *fiber = yafl_fiber_create(conditional_suspend_fiber_func, 16 * 1024, + 110 2 : yafl_fiber_t *fiber = yafl_fiber_create(conditional_suspend_fiber_func, 16 * 1024, 111 : YAFL_STACK_FLAGS_MALLOC); - 112 1 : assert(fiber != NULL); + 112 2 : assert(fiber != NULL); 113 : 114 : /* Suspend at i=0 */ - 115 1 : void *result = yafl_fiber_resume(fiber, (void *)0x1); - 116 1 : assert(result == (void *)0x100); + 115 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1); + 116 2 : assert(result == (void *)0x100); 117 : 118 : /* Suspend at i=2 */ - 119 1 : result = yafl_fiber_resume(fiber, (void *)0x2); - 120 1 : assert(result == (void *)0x102); + 119 2 : result = yafl_fiber_resume(fiber, (void *)0x2); + 120 2 : assert(result == (void *)0x102); 121 : 122 : /* Suspend at i=4 */ - 123 1 : result = yafl_fiber_resume(fiber, (void *)0x3); - 124 1 : assert(result == (void *)0x104); + 123 2 : result = yafl_fiber_resume(fiber, (void *)0x3); + 124 2 : assert(result == (void *)0x104); 125 : 126 : /* Complete */ - 127 1 : result = yafl_fiber_resume(fiber, (void *)0x4); - 128 1 : assert(result == (void *)0x4); - 129 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 127 2 : result = yafl_fiber_resume(fiber, (void *)0x4); + 128 2 : assert(result == (void *)0x4); + 129 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 130 : - 131 1 : yafl_fiber_destroy(fiber); + 131 2 : yafl_fiber_destroy(fiber); 132 : - 133 1 : printf("PASS\n"); - 134 1 : } + 133 2 : printf("PASS\n"); + 134 2 : } 135 : 136 : /* ======================================================================== 137 : * Test: NULL Data Passing 138 : * ======================================================================== */ 139 : - 140 1 : static void *null_data_fiber_func(void *arg) { - 141 1 : assert(arg == NULL); /* First resume passes NULL */ - 142 1 : arg = yafl_fiber_suspend(NULL); - 143 1 : assert(arg == NULL); /* Second resume passes NULL */ - 144 1 : return arg; + 140 2 : static void *null_data_fiber_func(void *arg) { + 141 2 : assert(arg == NULL); /* First resume passes NULL */ + 142 2 : arg = yafl_fiber_suspend(NULL); + 143 2 : assert(arg == NULL); /* Second resume passes NULL */ + 144 2 : return arg; 145 : } 146 : - 147 1 : static void test_null_data_passing(void) { - 148 1 : printf("test_null_data_passing: "); - 149 1 : fflush(stdout); + 147 2 : static void test_null_data_passing(void) { + 148 2 : printf("test_null_data_passing: "); + 149 2 : fflush(stdout); 150 : - 151 1 : yafl_fiber_t *fiber = yafl_fiber_create(null_data_fiber_func, 16 * 1024, + 151 2 : yafl_fiber_t *fiber = yafl_fiber_create(null_data_fiber_func, 16 * 1024, 152 : YAFL_STACK_FLAGS_MALLOC); - 153 1 : assert(fiber != NULL); + 153 2 : assert(fiber != NULL); 154 : - 155 1 : void *result = yafl_fiber_resume(fiber, NULL); - 156 1 : assert(result == NULL); + 155 2 : void *result = yafl_fiber_resume(fiber, NULL); + 156 2 : assert(result == NULL); 157 : - 158 1 : result = yafl_fiber_resume(fiber, NULL); - 159 1 : assert(result == NULL); + 158 2 : result = yafl_fiber_resume(fiber, NULL); + 159 2 : assert(result == NULL); 160 : - 161 1 : yafl_fiber_destroy(fiber); + 161 2 : yafl_fiber_destroy(fiber); 162 : - 163 1 : printf("PASS\n"); - 164 1 : } + 163 2 : printf("PASS\n"); + 164 2 : } 165 : 166 : /* ======================================================================== 167 : * Test: VMEM Allocation with Suspend/Resume 168 : * ======================================================================== */ 169 : - 170 1 : static void *vmem_test_func(void *arg) { - 171 1 : arg = yafl_fiber_suspend((void *)0xAAAA); - 172 1 : arg = yafl_fiber_suspend((void *)0xBBBB); - 173 1 : return arg; + 170 2 : static void *vmem_test_func(void *arg) { + 171 2 : arg = yafl_fiber_suspend((void *)0xAAAA); + 172 2 : arg = yafl_fiber_suspend((void *)0xBBBB); + 173 2 : return arg; 174 : } 175 : - 176 1 : static void test_vmem_suspend_resume(void) { - 177 1 : printf("test_vmem_suspend_resume: "); - 178 1 : fflush(stdout); + 176 2 : static void test_vmem_suspend_resume(void) { + 177 2 : printf("test_vmem_suspend_resume: "); + 178 2 : fflush(stdout); 179 : - 180 1 : yafl_fiber_t *fiber = yafl_fiber_create(vmem_test_func, 16 * 1024, + 180 2 : yafl_fiber_t *fiber = yafl_fiber_create(vmem_test_func, 16 * 1024, 181 : YAFL_STACK_FLAGS_VMEM); - 182 1 : assert(fiber != NULL); + 182 2 : assert(fiber != NULL); 183 : - 184 1 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); - 185 1 : assert(result == (void *)0xAAAA); + 184 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); + 185 2 : assert(result == (void *)0xAAAA); 186 : - 187 1 : result = yafl_fiber_resume(fiber, (void *)0x2222); - 188 1 : assert(result == (void *)0xBBBB); + 187 2 : result = yafl_fiber_resume(fiber, (void *)0x2222); + 188 2 : assert(result == (void *)0xBBBB); 189 : - 190 1 : result = yafl_fiber_resume(fiber, (void *)0x3333); - 191 1 : assert(result == (void *)0x3333); - 192 1 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 190 2 : result = yafl_fiber_resume(fiber, (void *)0x3333); + 191 2 : assert(result == (void *)0x3333); + 192 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 193 : - 194 1 : yafl_fiber_destroy(fiber); + 194 2 : yafl_fiber_destroy(fiber); 195 : - 196 1 : printf("PASS\n"); - 197 1 : } + 196 2 : printf("PASS\n"); + 197 2 : } 198 : 199 : /* ======================================================================== 200 : * Main 201 : * ======================================================================== */ 202 : - 203 1 : int main(void) { - 204 1 : printf("Running YAFL suspend/resume tests...\n"); + 203 2 : int main(void) { + 204 2 : printf("Running YAFL suspend/resume tests...\n"); 205 : - 206 1 : test_bidirectional_data_passing(); - 207 1 : test_multiple_suspend_points(); - 208 1 : test_conditional_suspension(); - 209 1 : test_null_data_passing(); - 210 1 : test_vmem_suspend_resume(); + 206 2 : test_bidirectional_data_passing(); + 207 2 : test_multiple_suspend_points(); + 208 2 : test_conditional_suspension(); + 209 2 : test_null_data_passing(); + 210 2 : test_vmem_suspend_resume(); 211 : - 212 1 : printf("\nAll tests passed!\n"); - 213 1 : return 0; + 212 2 : printf("\nAll tests passed!\n"); + 213 2 : return 0; 214 : } @@ -282,7 +282,7 @@
Current view:top level - tests - test_yafl_suspend_resume.c (source / functions)top level - tests - test_yafl_suspend_resume.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines: 100.0 %107107111111
Test Date:Generated2026-04-28 13:42:34 Functions: 100.0 %
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/streamline-plan.md b/docs/streamline-plan.md new file mode 100644 index 0000000..ec875d1 --- /dev/null +++ b/docs/streamline-plan.md @@ -0,0 +1,96 @@ +# Streamlining Summary + +## Summary + +The coroutine-layer streamlining work is implemented across the C runtime, assembly backends, and toolchain selection files. YAFL now uses `yafl_switch(yafl_t *save, yafl_t target, void *data)` and `yafl_make_context()` internally, which removes `transfer_t`, removes `pending_arg`, and keeps the hot path at a save/restore plus direct data handoff. + +The public API in `include/yafl.h` stays unchanged. All of the streamlining work remains below that surface. + +## Status + +- `src/yafl.c` now uses direct `void *` handoff through `yafl_switch`. +- Assembly sources and exported symbols are renamed to `switch_*` / `make_context_*` and `yafl_switch` / `yafl_make_context`. +- Toolchain `ASM_FILES` lists point at the renamed files. +- The host arm64 build and test suite pass after the refactor. + +## Implemented Design + +### Low-level switch primitive + +The internal switch ABI is now: + +`void *yafl_switch(yafl_t *save, yafl_t target, void *data)` + +- `*save` receives the current suspended context. +- `target` is the context to resume. +- `data` is delivered directly both as the resumed return value and as the first-entry argument. + +This removes the synthetic transport struct and the Windows-specific hidden struct-return plumbing that existed in the old backend model. + +### Initial context creation + +The initial saved context is created by: + +`yafl_t yafl_make_context(void *sp, size_t size, yafl_entry_t fn)` + +Each backend-specific `make_context_*` file prepares the initial stack/register image for a new fiber and installs the `finish:` fallback path that terminates the process if a fiber entry function unexpectedly returns through the raw frame. + +### C runtime changes + +- `yafl_transfer_t` is removed. +- `pending_arg` is removed from `struct yafl_fiber`. +- `fiber_entry_trampoline()` now receives the first resume argument directly and discovers the active fiber through TLS. +- `yafl_fiber_resume()` and `yafl_fiber_suspend()` use direct `void *` handoff through `yafl_switch()`. +- Fiber stack cleanup and watermark reinitialization are both routed through helpers so failure paths stay linear. + +### Assembly and naming changes + +- All internal switch-family files are renamed from `jump_*` to `switch_*`. +- All initial-context files are renamed from `make_*` to `make_context_*`. +- Exported assembly symbols are renamed from `jump_fcontext` / `make_fcontext` to `yafl_switch` / `yafl_make_context`. +- Toolchain `ASM_FILES` lists in `toolchains/*.cmake` point at the renamed files. + +### Backend scope + +The direct-data `yafl_switch` model is implemented across the maintained backends in the tree, including x86_64, arm64, i386, arm, riscv64, loongarch64, mips32, mips64, ppc32, ppc64, s390x, and sparc64. + +The guiding backend rule is unchanged across architectures: save-via-pointer, restore-target-from-second-argument, and pass `data` directly. + +## Validation + +- Host arm64 library build succeeds. +- Host tests pass: `test_yafl_basic`, `test_yafl_suspend_resume`, `test_yafl_guard`, `test_yafl_many`, and `test_yafl_watermark`. +- Maintained sources no longer carry live `transfer_t`, `pending_arg`, `jump_fcontext`, or `make_fcontext` implementation references. +- Non-host assembly validation is opportunistic and depends on the locally available target assemblers. + +## Remaining Follow-up + +- Keep generated coverage artifacts in sync with the source tree when coverage is regenerated. +- Expand cross-target validation when additional target toolchains are available. + +## Decisions + +1. **Do not move the C trampoline into assembly** + - That would hardcode `struct yafl_fiber` offsets into every backend. + - The resulting code would be harder to audit and not meaningfully faster. + +2. **Do not collapse the initial-context primitive and the switch primitive into one assembly entry point** + - `yafl_switch` is the hot path. + - `yafl_make_context` runs only during fiber creation. + - Combining them would trade a tiny amount of source reduction for a branchier and less legible hot path. + +3. **Do not merge ELF and Mach-O assembly files even when they currently match** + - OS-level context requirements may diverge later, including support for wider architectural state such as AVX or other OS-managed register state. + - Keep separate per-OS source files so those changes can land without re-splitting shared assembly later. + +4. **Do not introduce runtime architecture dispatch to reduce file count** + - The current per-target selection in the toolchain files keeps the build explicit and predictable. + - Runtime selection would increase complexity without helping performance. + +## Relevant Files + +- `src/yafl.c` — current C runtime implementation of the streamlined switch semantics. +- `include/yafl.h` — no public API changes are required. +- `src/asm/*/switch_*` — backend switch-family sources implementing `yafl_switch`. +- `src/asm/*/make_context_*` — backend entry-context sources implementing `yafl_make_context`. +- `toolchains/*.cmake` — backend selection for the renamed assembly sources. diff --git a/scripts/generate_coverage.sh b/scripts/generate_coverage.sh index f4a5634..17abd7b 100755 --- a/scripts/generate_coverage.sh +++ b/scripts/generate_coverage.sh @@ -54,7 +54,7 @@ lcov --capture --directory build --output-file coverage.info echo echo -e "${GREEN}Step 5: Filtering system files from coverage...${NC}" -lcov --remove coverage.info '/usr/*' --output-file coverage.info +lcov --remove coverage.info '/usr/*' --output-file coverage.info --ignore-errors unused echo echo -e "${GREEN}Step 6: Generating HTML report...${NC}" diff --git a/scripts/make_coverage_badge.sh b/scripts/make_coverage_badge.sh old mode 100644 new mode 100755 diff --git a/src/asm/README.md b/src/asm/README.md index 5646ed6..6064360 100644 --- a/src/asm/README.md +++ b/src/asm/README.md @@ -4,49 +4,70 @@ This directory contains architecture-specific assembly code for context switchin ## File Organization -``` +```text asm/ -├── x86_64/ # 64-bit Intel/AMD +├── arm/ # 32-bit ARM ├── arm64/ # 64-bit ARM (AArch64) ├── i386/ # 32-bit Intel -└── arm/ # 32-bit ARM +├── loongarch64/ # 64-bit LoongArch +├── mips/ # 32-bit MIPS +├── mips64/ # 64-bit MIPS N64 ABI +├── ppc32/ # 32-bit PowerPC +├── ppc64/ # 64-bit PowerPC +├── riscv32/ # 32-bit RISC-V notes +├── riscv64/ # 64-bit RISC-V +├── s390x/ # IBM Z / s390x +├── sparc64/ # 64-bit SPARC +└── x86_64/ # 64-bit Intel/AMD ``` ## File Naming Convention -Each architecture has three types of functions: +Each architecture has two low-level entry points: -1. **make_*_*.S** - Create/initialize a context +1. **make_context_*_*.S** - Create/initialize a context - Sets up stack frame and instruction pointer - Called once per new context - - Maps to: `yafl_t make_yafl(void *sp, size_t size, yafl_fn_t fn)` + - Maps to: `yafl_t yafl_make_context(void *sp, size_t size, yafl_fn_t fn)` -2. **jump_*_*.S** - Switch to a context +2. **switch_*_*.S** - Switch to a context - Saves current state, restores new context - Called frequently (on every context switch) - - Maps to: `yafl_transfer_t jump_yafl(yafl_t const to, void *vp)` + - Maps to: `void *yafl_switch(yafl_t *save, yafl_t target, void *data)` ## ABI and OS-Specific Suffixes Filenames follow the pattern: `[function]_[arch]_[abi]_[os]_[asm].S` ### Architectures + - `x86_64` - Intel/AMD 64-bit - `arm64` - ARM 64-bit (AArch64) - `i386` - Intel/AMD 32-bit - `arm` - ARM 32-bit +- `loongarch64` - LoongArch 64-bit +- `mips` - MIPS 32-bit O32 ABI +- `mips64` - MIPS 64-bit N64 ABI +- `ppc32` - PowerPC 32-bit SysV ABI +- `ppc64` - PowerPC 64-bit SysV ABI +- `riscv64` - RISC-V 64-bit SysV ABI +- `s390x` - IBM Z / s390x SysV ABI +- `sparc64` - SPARC V9 64-bit SysV ABI ### Application Binary Interface (ABI) + - `sysv` - System V ABI (Linux, BSD, older UNIX) - `aapcs` - ARM EABI / ARM Architecture Procedure Call Standard (ARM systems) - `ms` - Microsoft x64 ABI (Windows - not used in this project) ### Operating System / Format + - `elf` - ELF executable format (Linux) - `macho` - Mach-O executable format (macOS) - `xcoff` - XCOFF executable format (IBM PowerPC AIX - not in tier 1) ### Assembler + - `gas` - GNU Assembler syntax - `masm` - Microsoft Assembler (MASM) syntax - not used in this project - `armasm` - ARM Assembler syntax - not used in this project @@ -56,21 +77,26 @@ Filenames follow the pattern: `[function]_[arch]_[abi]_[os]_[asm].S` The build system automatically selects the correct files based on `uname -s` and `uname -m`: ### macOS (Darwin) -- **arm64**: `*_arm64_aapcs_macho_gas.S` -- **x86_64**: `*_x86_64_sysv_macho_gas.S` -- **i386**: `*_i386_sysv_macho_gas.S` -- **arm**: `*_arm_aapcs_macho_gas.S` + +- **arm64**: `make_context_arm64_aapcs_macho_gas.S`, `switch_arm64_aapcs_macho_gas.S` +- **x86_64**: `make_context_x86_64_sysv_macho_gas.S`, `switch_x86_64_sysv_macho_gas.S` +- **i386**: `make_context_i386_sysv_macho_gas.S`, `switch_i386_sysv_macho_gas.S` +- **arm**: `make_context_arm_aapcs_macho_gas.S`, `switch_arm_aapcs_macho_gas.S` ### Linux -- **x86_64**: `*_x86_64_sysv_elf_gas.S` -- **aarch64**: `*_arm64_aapcs_elf_gas.S` -- **i386**: `*_i386_sysv_elf_gas.S` -- **armv7l**: `*_arm_aapcs_elf_gas.S` + +- **x86_64**: `make_context_x86_64_sysv_elf_gas.S`, `switch_x86_64_sysv_elf_gas.S` +- **aarch64**: `make_context_arm64_aapcs_elf_gas.S`, `switch_arm64_aapcs_elf_gas.S` +- **i386**: `make_context_i386_sysv_elf_gas.S`, `switch_i386_sysv_elf_gas.S` +- **armv7l**: `make_context_arm_aapcs_elf_gas.S`, `switch_arm_aapcs_elf_gas.S` + +Additional Linux-targeted toolchain files in this repository select backend pairs for `mipsel`, `mips64el`, `powerpc`, `powerpc64le`, `riscv64`, `s390x`, and `sparc64`. ## Source and License All files are extracted directly from Boost.Context repository: -https://github.com/boostorg/context/tree/develop/src/asm + + Each file retains its original Boost Software License 1.0 header: @@ -86,23 +112,28 @@ Each file retains its original Boost Software License 1.0 header: ## How They Work (High Level) -### make_* Functions +### make_context_* Functions + Create a new execution context by: + 1. Storing the stack pointer and context metadata 2. Setting up the instruction pointer to the entry function 3. Initializing CPU registers to safe values 4. Returning an opaque handle to the new context -### jump_* Functions +### switch_* Functions + Context switch by: + 1. Saving all CPU registers to current stack 2. Restoring all CPU registers from target context's stack 3. Returning to execution at target context's saved instruction pointer -4. The "return value" includes the previous context and user data +4. Returning the caller-supplied `data` pointer directly while saving the previous context through the `save` pointer ### Calling Convention Details -The entry function for a new context receives `yafl_transfer_t` in: +The entry function for a new context receives the first resume argument directly in: + - **x86_64**: `rdi` register (System V ABI first argument) - **arm64**: `x0` register (AAPCS first argument) - **i386**: Stack parameter (32-bit calling convention) @@ -111,9 +142,12 @@ The entry function for a new context receives `yafl_transfer_t` in: ## Testing and Validation All assembly files are tested by: + 1. `test_yafl_basic` - Basic context creation and switching -2. `test_yafl_simple` - Simple entry point execution -3. `test_yafl_transfer` - Data passing through context switches +2. `test_yafl_suspend_resume` - Suspend/resume handoff behavior +3. `test_yafl_guard` - Guard-page stack protection +4. `test_yafl_many` - Repeated fiber creation and switching +5. `test_yafl_watermark` - Watermark-based stack usage tracking Tests are compiled with the selected architecture's assembly files and run on the target platform. @@ -123,16 +157,21 @@ To add a new architecture (e.g., RISC-V): 1. Create new subdirectory: `riscv64/` 2. Copy assembly files from Boost.Context: - - `make_riscv64_sysv_elf_gas.S` - - `jump_riscv64_sysv_elf_gas.S` + - `make_context_riscv64_sysv_elf_gas.S` + - `switch_riscv64_sysv_elf_gas.S` 3. Update `Makefile` to detect the new architecture 4. Update `CMakeLists.txt` similarly 5. Test on actual hardware or in emulation +## Current Repository Notes + +- `riscv32/` currently contains notes only and is not selected by any checked-in toolchain file. +- Several backend directories beyond the host architecture are maintained primarily through assembly syntax checks and cross-target builds when the corresponding toolchains are available. + ## References -- **Boost.Context Source**: https://github.com/boostorg/context -- **System V ABI**: https://software.intel.com/en-us/sites/default/files/article/2016-08/x86-64-abi-0.99.pdf -- **ARM EABI**: https://github.com/ARM-software/abi-aa -- **ELF Format**: https://refspecs.linuxbase.org/elf/elf.pdf -- **Mach-O Format**: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachORuntime/ +- **Boost.Context Source**: +- **System V ABI**: +- **ARM EABI**: +- **ELF Format**: +- **Mach-O Format**: diff --git a/src/asm/arm/make_arm_aapcs_elf_gas.S b/src/asm/arm/make_context_arm_aapcs_elf_gas.S similarity index 90% rename from src/asm/arm/make_arm_aapcs_elf_gas.S rename to src/asm/arm/make_context_arm_aapcs_elf_gas.S index 9616e56..f9993db 100644 --- a/src/asm/arm/make_arm_aapcs_elf_gas.S +++ b/src/asm/arm/make_context_arm_aapcs_elf_gas.S @@ -40,26 +40,21 @@ .file "make_arm_aapcs_elf_gas.S" .text -.globl make_fcontext -.hidden make_fcontext +.globl yafl_make_context +.hidden yafl_make_context .align 2 -.type make_fcontext,%function +.type yafl_make_context,%function .syntax unified -make_fcontext: +yafl_make_context: @ shift address in A1 to lower 16 byte boundary bic a1, a1, #15 @ reserve space for context-data on context-stack sub a1, a1, #124 - @ third arg of make_fcontext() == address of context-function + @ third arg of yafl_make_context() == address of context-function str a3, [a1, #104] - @ compute address of returned transfer_t - add a2, a1, #108 - mov a3, a2 - str a3, [a1, #64] - @ compute abs address of label finish adr a2, finish @ save address of finish as return-address for context-function @@ -76,7 +71,7 @@ finish: mov a1, #0 @ exit application bl _exit@PLT -.size make_fcontext,.-make_fcontext +.size yafl_make_context,.-yafl_make_context @ Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/arm/make_arm_aapcs_macho_gas.S b/src/asm/arm/make_context_arm_aapcs_macho_gas.S similarity index 91% rename from src/asm/arm/make_arm_aapcs_macho_gas.S rename to src/asm/arm/make_context_arm_aapcs_macho_gas.S index de93407..ef71cc7 100644 --- a/src/asm/arm/make_arm_aapcs_macho_gas.S +++ b/src/asm/arm/make_context_arm_aapcs_macho_gas.S @@ -39,24 +39,19 @@ *******************************************************/ .text -.private_extern _make_fcontext -.globl _make_fcontext +.private_extern _yafl_make_context +.globl _yafl_make_context .align 2 -_make_fcontext: +_yafl_make_context: @ shift address in A1 to lower 16 byte boundary bic a1, a1, #15 @ reserve space for context-data on context-stack sub a1, a1, #124 - @ third arg of make_fcontext() == address of context-function + @ third arg of yafl_make_context() == address of context-function str a3, [a1, #108] - @ compute address of returned transfer_t - add a2, a1, #112 - mov a3, a2 - str a3, [a1, #68] - @ compute abs address of label finish adr a2, finish @ save address of finish as return-address for context-function diff --git a/src/asm/arm/jump_arm_aapcs_elf_gas.S b/src/asm/arm/switch_arm_aapcs_elf_gas.S similarity index 84% rename from src/asm/arm/jump_arm_aapcs_elf_gas.S rename to src/asm/arm/switch_arm_aapcs_elf_gas.S index 9934c08..f449b17 100644 --- a/src/asm/arm/jump_arm_aapcs_elf_gas.S +++ b/src/asm/arm/switch_arm_aapcs_elf_gas.S @@ -40,15 +40,15 @@ .file "jump_arm_aapcs_elf_gas.S" .text -.globl jump_fcontext -.hidden jump_fcontext +.globl yafl_switch +.hidden yafl_switch .align 2 -.type jump_fcontext,%function +.type yafl_switch,%function .syntax unified -jump_fcontext: +yafl_switch: @ save LR as PC push {lr} - @ save hidden,V1-V8,LR + @ save V1-V8,LR and preserve layout slot for the old hidden pointer push {a1,v1-v8,lr} @ prepare stack for FPU @@ -58,10 +58,10 @@ jump_fcontext: vstmia sp, {d8-d15} #endif - @ store RSP (pointing to context-data) in A1 - mov a1, sp + @ save current context through the first argument pointer + str sp, [a1] - @ restore RSP (pointing to context-data) from A2 + @ restore target context from the second argument mov sp, a2 #if (defined(__VFP_FP__) && !defined(__SOFTFP__)) @@ -71,19 +71,15 @@ jump_fcontext: @ prepare stack for FPU add sp, sp, #64 - @ restore hidden,V1-V8,LR + @ restore V1-V8,LR pop {a4,v1-v8,lr} - @ return transfer_t from jump - str a1, [a4, #0] - str a3, [a4, #4] - @ pass transfer_t as first arg in context function - @ A1 == FCTX, A2 == DATA - mov a2, a3 + @ return and pass data directly + mov a1, a3 @ restore PC pop {pc} -.size jump_fcontext,.-jump_fcontext +.size yafl_switch,.-yafl_switch @ Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/arm/jump_arm_aapcs_macho_gas.S b/src/asm/arm/switch_arm_aapcs_macho_gas.S similarity index 86% rename from src/asm/arm/jump_arm_aapcs_macho_gas.S rename to src/asm/arm/switch_arm_aapcs_macho_gas.S index 44e7f2a..5ee3456 100644 --- a/src/asm/arm/jump_arm_aapcs_macho_gas.S +++ b/src/asm/arm/switch_arm_aapcs_macho_gas.S @@ -39,13 +39,13 @@ *******************************************************/ .text -.private_extern _jump_fcontext -.globl _jump_fcontext +.private_extern _yafl_switch +.globl _yafl_switch .align 2 -_jump_fcontext: +_yafl_switch: @ save LR as PC push {lr} - @ save hidden,V1-V8,LR + @ save V1-V8,LR and preserve layout slot for the old hidden pointer push {a1,v1-v8,lr} @ locate TLS to save/restore SjLj handler @@ -64,10 +64,10 @@ _jump_fcontext: vstmia sp, {d8-d15} #endif - @ store RSP (pointing to context-data) in A1 - mov a1, sp + @ save current context through the first argument pointer + str sp, [a1] - @ restore RSP (pointing to context-data) from A2 + @ restore target context from the second argument mov sp, a2 #if (defined(__VFP_FP__) && !defined(__SOFTFP__)) @@ -82,15 +82,11 @@ _jump_fcontext: @ store SjLj handler in TLS str v1, [v2, #72] - @ restore hidden,V1-V8,LR + @ restore V1-V8,LR pop {a4,v1-v8,lr} - @ return transfer_t from jump - str a1, [a4, #0] - str a3, [a4, #4] - @ pass transfer_t as first arg in context function - @ A1 == FCTX, A2 == DATA - mov a2, a3 + @ return and pass data directly + mov a1, a3 @ restore PC pop {pc} diff --git a/src/asm/arm64/make_arm64_aapcs_elf_gas.S b/src/asm/arm64/make_context_arm64_aapcs_elf_gas.S similarity index 95% rename from src/asm/arm64/make_arm64_aapcs_elf_gas.S rename to src/asm/arm64/make_context_arm64_aapcs_elf_gas.S index 2465254..335247f 100644 --- a/src/asm/arm64/make_arm64_aapcs_elf_gas.S +++ b/src/asm/arm64/make_context_arm64_aapcs_elf_gas.S @@ -69,10 +69,10 @@ .popsection #endif .align 2 -.global make_fcontext -.hidden make_fcontext -.type make_fcontext, %function -make_fcontext: +.global yafl_make_context +.hidden yafl_make_context +.type yafl_make_context, %function +yafl_make_context: #if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1) hint #34 /* bti c */ #endif @@ -82,7 +82,7 @@ make_fcontext: # reserve space for context-data on context-stack sub x0, x0, #0xb0 - # third arg of make_fcontext() == address of context-function + # third arg of yafl_make_context() == address of context-function # store address as a PC to jump in str x2, [x0, #0xa0] @@ -99,6 +99,6 @@ finish: # exit application bl _exit -.size make_fcontext,.-make_fcontext +.size yafl_make_context,.-yafl_make_context # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/arm64/make_arm64_aapcs_macho_gas.S b/src/asm/arm64/make_context_arm64_aapcs_macho_gas.S similarity index 95% rename from src/asm/arm64/make_arm64_aapcs_macho_gas.S rename to src/asm/arm64/make_context_arm64_aapcs_macho_gas.S index a6a1314..e6c5db9 100644 --- a/src/asm/arm64/make_arm64_aapcs_macho_gas.S +++ b/src/asm/arm64/make_context_arm64_aapcs_macho_gas.S @@ -52,18 +52,18 @@ *******************************************************/ .text -.private_extern _make_fcontext -.globl _make_fcontext +.private_extern _yafl_make_context +.globl _yafl_make_context .balign 16 -_make_fcontext: +_yafl_make_context: ; shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF ; reserve space for context-data on context-stack sub x0, x0, #0xb0 - ; third arg of make_fcontext() == address of context-function + ; third arg of yafl_make_context() == address of context-function ; store address as a PC to jump in str x2, [x0, #0xa0] diff --git a/src/asm/arm64/make_arm64_aapcs_pe_armasm.asm b/src/asm/arm64/make_context_arm64_aapcs_pe_armasm.asm similarity index 97% rename from src/asm/arm64/make_arm64_aapcs_pe_armasm.asm rename to src/asm/arm64/make_context_arm64_aapcs_pe_armasm.asm index c384af0..232bdd7 100644 --- a/src/asm/arm64/make_arm64_aapcs_pe_armasm.asm +++ b/src/asm/arm64/make_context_arm64_aapcs_pe_armasm.asm @@ -58,10 +58,10 @@ ;******************************************************* AREA |.text|, CODE, READONLY, ALIGN=4, CODEALIGN - EXPORT make_fcontext + EXPORT yafl_make_context IMPORT _exit -make_fcontext proc +yafl_make_context proc ; save stack top address to x3 mov x3, x0 @@ -79,7 +79,7 @@ make_fcontext proc ; save 0 as 'fiber data' str xzr, [x0, #0xb8] - ; third arg of make_fcontext() == address of context-function + ; third arg of yafl_make_context() == address of context-function ; store address as x19 for trampoline str x2, [x0, #0x40] ; store trampoline address as pc diff --git a/src/asm/arm64/make_arm64_aapcs_pe_armclang.S b/src/asm/arm64/make_context_arm64_aapcs_pe_armclang.S similarity index 95% rename from src/asm/arm64/make_arm64_aapcs_pe_armclang.S rename to src/asm/arm64/make_context_arm64_aapcs_pe_armclang.S index 761092f..105cccb 100644 --- a/src/asm/arm64/make_arm64_aapcs_pe_armclang.S +++ b/src/asm/arm64/make_context_arm64_aapcs_pe_armclang.S @@ -63,10 +63,10 @@ .text .balign 4 -.globl make_fcontext -.def make_fcontext; .scl 2; .type 32; .endef -.seh_proc make_fcontext -make_fcontext: +.globl yafl_make_context +.def yafl_make_context; .scl 2; .type 32; .endef +.seh_proc yafl_make_context +yafl_make_context: .seh_endprologue // save stack top address to x3 mov x3, x0 @@ -85,7 +85,7 @@ make_fcontext: // save 0 as 'fiber data' str xzr, [x0, #0xb8] - // third arg of make_fcontext() == address of context-function + // third arg of yafl_make_context() == address of context-function // store address as x19 for trampoline str x2, [x0, #0x40] // store trampoline address as pc @@ -115,4 +115,4 @@ finish: .def _exit; .scl 2; .type 32; .endef /* standard C library function */ .section .drectve -.ascii " -export:\"make_fcontext\"" +.ascii " -export:\"yafl_make_context\"" diff --git a/src/asm/arm64/jump_arm64_aapcs_elf_gas.S b/src/asm/arm64/switch_arm64_aapcs_elf_gas.S similarity index 92% rename from src/asm/arm64/jump_arm64_aapcs_elf_gas.S rename to src/asm/arm64/switch_arm64_aapcs_elf_gas.S index b8abb2e..f2cfd01 100644 --- a/src/asm/arm64/jump_arm64_aapcs_elf_gas.S +++ b/src/asm/arm64/switch_arm64_aapcs_elf_gas.S @@ -69,10 +69,10 @@ .popsection #endif .align 2 -.global jump_fcontext -.hidden jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global yafl_switch +.hidden yafl_switch +.type yafl_switch, %function +yafl_switch: #if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1) hint #34 /* bti c: valid indirect-entry target */ #endif @@ -96,11 +96,13 @@ jump_fcontext: # save LR as PC str x30, [sp, #0xa0] - # store RSP (pointing to context-data) in X0 - mov x4, sp + # save current context through the first argument pointer + mov x4, x2 + mov x5, sp + str x5, [x0] - # restore RSP (pointing to context-data) from X1 - mov sp, x0 + # restore target context from the second argument + mov sp, x1 # load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -116,9 +118,7 @@ jump_fcontext: ldp x27, x28, [sp, #0x80] ldp x29, x30, [sp, #0x90] - # return transfer_t from jump - # pass transfer_t as first arg in context function - # X0 == FCTX, X1 == DATA + # return and pass data directly mov x0, x4 # load pc @@ -128,6 +128,6 @@ jump_fcontext: add sp, sp, #0xb0 ret x4 -.size jump_fcontext,.-jump_fcontext +.size yafl_switch,.-yafl_switch # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/arm64/jump_arm64_aapcs_macho_gas.S b/src/asm/arm64/switch_arm64_aapcs_macho_gas.S similarity index 92% rename from src/asm/arm64/jump_arm64_aapcs_macho_gas.S rename to src/asm/arm64/switch_arm64_aapcs_macho_gas.S index 12cc021..eef7d50 100644 --- a/src/asm/arm64/jump_arm64_aapcs_macho_gas.S +++ b/src/asm/arm64/switch_arm64_aapcs_macho_gas.S @@ -52,10 +52,10 @@ *******************************************************/ .text -.private_extern _jump_fcontext -.globl _jump_fcontext +.private_extern _yafl_switch +.globl _yafl_switch .balign 16 -_jump_fcontext: +_yafl_switch: ; prepare stack for GP + FPU sub sp, sp, #0xb0 @@ -76,11 +76,12 @@ _jump_fcontext: ; save LR as PC str lr, [sp, #0xa0] - ; store RSP (pointing to context-data) in X0 + ; save current SP (pointing to context-data) through X0 mov x4, sp + str x4, [x0] - ; restore RSP (pointing to context-data) from X1 - mov sp, x0 + ; restore SP (pointing to context-data) from X1 + mov sp, x1 ; load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -96,10 +97,8 @@ _jump_fcontext: ldp x27, x28, [sp, #0x80] ldp fp, lr, [sp, #0x90] - ; return transfer_t from jump - ; pass transfer_t as first arg in context function - ; X0 == FCTX, X1 == DATA - mov x0, x4 + ; return data as the resumed function result and first argument + mov x0, x2 ; load pc ldr x4, [sp, #0xa0] diff --git a/src/asm/arm64/jump_arm64_aapcs_pe_armasm.asm b/src/asm/arm64/switch_arm64_aapcs_pe_armasm.asm similarity index 94% rename from src/asm/arm64/jump_arm64_aapcs_pe_armasm.asm rename to src/asm/arm64/switch_arm64_aapcs_pe_armasm.asm index afb6d2b..f1b058c 100644 --- a/src/asm/arm64/jump_arm64_aapcs_pe_armasm.asm +++ b/src/asm/arm64/switch_arm64_aapcs_pe_armasm.asm @@ -58,9 +58,9 @@ ;******************************************************* AREA |.text|, CODE, READONLY, ALIGN=4, CODEALIGN - EXPORT jump_fcontext + EXPORT yafl_switch -jump_fcontext proc +yafl_switch proc ; prepare stack for GP + FPU sub sp, sp, #0xd0 @@ -89,11 +89,13 @@ jump_fcontext proc ldr x6, [x18, #0x20] ; TeFiberData at ksarm64.h stp x5, x6, [sp, #0xb0] - ; store RSP (pointing to context-data) in X0 - mov x4, sp + ; save current context through the first argument pointer + mov x4, x2 + mov x7, sp + str x7, [x0] - ; restore RSP (pointing to context-data) from X1 - mov sp, x0 + ; restore target context from the second argument + mov sp, x1 ; restore stack base and limit ldp x5, x6, [sp, #0xa0] @@ -117,9 +119,7 @@ jump_fcontext proc ldp x27, x28, [sp, #0x80] ldp x29, x30, [sp, #0x90] - ; return transfer_t from jump - ; pass transfer_t as first arg in context function - ; X0 == FCTX, X1 == DATA + ; return and pass data directly mov x0, x4 ; load pc diff --git a/src/asm/arm64/jump_arm64_aapcs_pe_armclang.S b/src/asm/arm64/switch_arm64_aapcs_pe_armclang.S similarity index 92% rename from src/asm/arm64/jump_arm64_aapcs_pe_armclang.S rename to src/asm/arm64/switch_arm64_aapcs_pe_armclang.S index 3a62562..8cb5134 100644 --- a/src/asm/arm64/jump_arm64_aapcs_pe_armclang.S +++ b/src/asm/arm64/switch_arm64_aapcs_pe_armclang.S @@ -63,9 +63,9 @@ .text .balign 4 -.def jump_fcontext; .scl 2; .type 32; .endef -.seh_proc jump_fcontext -jump_fcontext: +.def yafl_switch; .scl 2; .type 32; .endef +.seh_proc yafl_switch +yafl_switch: .seh_endprologue // prepare stack for GP + FPU sub sp, sp, #0xd0 @@ -95,11 +95,13 @@ jump_fcontext: ldr x6, [x18, #0x20] // TeFiberData at ksarm64.h stp x5, x6, [sp, #0xb0] - // store RSP (pointing to context-data) in X0 - mov x4, sp + // save current context through the first argument pointer + mov x4, x2 + mov x7, sp + str x7, [x0] - // restore RSP (pointing to context-data) from X1 - mov sp, x0 + // restore target context from the second argument + mov sp, x1 // restore stack base and limit ldp x5, x6, [sp, #0xa0] @@ -123,9 +125,7 @@ jump_fcontext: ldp x27, x28, [sp, #0x80] ldp x29, x30, [sp, #0x90] - // return transfer_t from jump - // pass transfer_t as first arg in context function - // X0 == FCTX, X1 == DATA + // return and pass data directly mov x0, x4 // load pc @@ -138,4 +138,4 @@ jump_fcontext: .seh_endproc .section .drectve -.ascii " -export:\"jump_fcontext\"" +.ascii " -export:\"yafl_switch\"" diff --git a/src/asm/i386/make_i386_sysv_elf_gas.S b/src/asm/i386/make_context_i386_sysv_elf_gas.S similarity index 87% rename from src/asm/i386/make_i386_sysv_elf_gas.S rename to src/asm/i386/make_context_i386_sysv_elf_gas.S index 56c3bdc..e30b990 100644 --- a/src/asm/i386/make_i386_sysv_elf_gas.S +++ b/src/asm/i386/make_context_i386_sysv_elf_gas.S @@ -26,12 +26,12 @@ .file "make_i386_sysv_elf_gas.S" .text -.globl make_fcontext -.hidden make_fcontext +.globl yafl_make_context +.hidden yafl_make_context .align 2 -.type make_fcontext,@function -make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +.type yafl_make_context,@function +yafl_make_context: + /* first arg of yafl_make_context() == top of context-stack */ movl 0x4(%esp), %eax /* reserve space for first argument of context-function @@ -44,7 +44,7 @@ make_fcontext: /* reserve space for context-data on context-stack, and align the stack */ leal -0x34(%eax), %eax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of yafl_make_context() == address of context-function */ /* stored in EBX */ movl 0xc(%esp), %ecx movl %ecx, 0x14(%eax) @@ -60,11 +60,6 @@ make_fcontext: movl %ecx, 0x8(%eax) /* save stack guard */ #endif - /* return transport_t */ - /* FCTX == EDI, DATA == ESI */ - leal 0xc(%eax), %ecx - movl %ecx, 0x20(%eax) - /* compute abs address of label trampoline */ call 1f /* address of trampoline 1 */ @@ -72,7 +67,7 @@ make_fcontext: /* compute abs address of label trampoline */ addl $trampoline-1b, %ecx /* save address of trampoline as return address */ - /* will be entered after calling jump_fcontext() first time */ + /* will be entered after calling yafl_switch() first time */ movl %ecx, 0x1c(%eax) /* compute abs address of label finish */ @@ -88,9 +83,8 @@ make_fcontext: ret /* return pointer to context-data */ trampoline: - /* move transport_t for entering context-function */ - movl %edi, (%esp) - movl %esi, 0x4(%esp) + /* move the first-entry argument into the reserved stack slot */ + movl %eax, (%esp) pushl %ebp /* jump to context-function */ jmp *%ebx @@ -108,7 +102,7 @@ finish: /* exit application */ call _exit@PLT hlt -.size make_fcontext,.-make_fcontext +.size yafl_make_context,.-yafl_make_context /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/i386/make_i386_sysv_macho_gas.S b/src/asm/i386/make_context_i386_sysv_macho_gas.S similarity index 90% rename from src/asm/i386/make_i386_sysv_macho_gas.S rename to src/asm/i386/make_context_i386_sysv_macho_gas.S index 0107612..b3e4fd8 100644 --- a/src/asm/i386/make_i386_sysv_macho_gas.S +++ b/src/asm/i386/make_context_i386_sysv_macho_gas.S @@ -25,11 +25,11 @@ ****************************************************************************************/ .text -.private_extern _make_fcontext -.globl _make_fcontext +.private_extern _yafl_make_context +.globl _yafl_make_context .align 2 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +_yafl_make_context: + /* first arg of yafl_make_context() == top of context-stack */ movl 0x4(%esp), %eax /* reserve space for first argument of context-function @@ -42,7 +42,7 @@ _make_fcontext: /* reserve space for context-data on context-stack, and align the stack */ leal -0x34(%eax), %eax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of yafl_make_context() == address of context-function */ /* stored in EBX */ movl 0xc(%esp), %ecx movl %ecx, 0x10(%eax) @@ -59,7 +59,7 @@ _make_fcontext: /* compute abs address of label trampoline */ addl $trampoline-1b, %ecx /* save address of trampoline as return address */ - /* will be entered after calling jump_fcontext() first time */ + /* will be entered after calling yafl_switch() first time */ movl %ecx, 0x18(%eax) /* compute abs address of label finish */ @@ -75,9 +75,8 @@ _make_fcontext: ret /* return pointer to context-data */ trampoline: - /* move transport_t for entering context-function */ + /* move the first-entry argument into the reserved stack slot */ movl %eax, (%esp) - movl %edx, 0x4(%esp) pushl %ebp /* jump to context-function */ jmp *%ebx diff --git a/src/asm/i386/jump_i386_sysv_elf_gas.S b/src/asm/i386/switch_i386_sysv_elf_gas.S similarity index 86% rename from src/asm/i386/jump_i386_sysv_elf_gas.S rename to src/asm/i386/switch_i386_sysv_elf_gas.S index 727d661..e00444c 100644 --- a/src/asm/i386/jump_i386_sysv_elf_gas.S +++ b/src/asm/i386/switch_i386_sysv_elf_gas.S @@ -26,11 +26,11 @@ .file "jump_i386_sysv_elf_gas.S" .text -.globl jump_fcontext -.hidden jump_fcontext +.globl yafl_switch +.hidden yafl_switch .align 2 -.type jump_fcontext,@function -jump_fcontext: +.type yafl_switch,@function +yafl_switch: leal -0x1c(%esp), %esp /* prepare stack */ #if !defined(BOOST_USE_TSX) @@ -48,25 +48,18 @@ jump_fcontext: movl %ebx, 0x14(%esp) /* save EBX */ movl %ebp, 0x18(%esp) /* save EBP */ - /* store ESP (pointing to context-data) in ECX */ + /* save current context through the first argument pointer */ movl %esp, %ecx + movl 0x20(%esp), %eax + movl %ecx, (%eax) - /* first arg of jump_fcontext() == fcontext to jump to */ + /* target context and switch data */ movl 0x24(%esp), %eax - - /* second arg of jump_fcontext() == data to be transferred */ movl 0x28(%esp), %edx - /* restore ESP (pointing to context-data) from EAX */ + /* restore ESP (pointing to context-data) from target */ movl %eax, %esp - /* address of returned transport_t */ - movl 0x20(%esp), %eax - /* return parent fcontext_t */ - movl %ecx, (%eax) - /* return data */ - movl %edx, 0x4(%eax) - movl 0x1c(%esp), %ecx /* restore EIP */ #if !defined(BOOST_USE_TSX) @@ -86,9 +79,12 @@ jump_fcontext: leal 0x24(%esp), %esp /* prepare stack */ + /* return and pass data directly */ + movl %edx, %eax + /* jump to context */ jmp *%ecx -.size jump_fcontext,.-jump_fcontext +.size yafl_switch,.-yafl_switch /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/i386/jump_i386_sysv_macho_gas.S b/src/asm/i386/switch_i386_sysv_macho_gas.S similarity index 86% rename from src/asm/i386/jump_i386_sysv_macho_gas.S rename to src/asm/i386/switch_i386_sysv_macho_gas.S index 4cae325..9a19c90 100644 --- a/src/asm/i386/jump_i386_sysv_macho_gas.S +++ b/src/asm/i386/switch_i386_sysv_macho_gas.S @@ -25,10 +25,10 @@ ****************************************************************************************/ .text -.private_extern _jump_fcontext -.globl _jump_fcontext +.private_extern _yafl_switch +.globl _yafl_switch .align 2 -_jump_fcontext: +_yafl_switch: leal -0x18(%esp), %esp /* prepare stack */ #if !defined(BOOST_USE_TSX) @@ -41,22 +41,18 @@ _jump_fcontext: movl %ebx, 0x10(%esp) /* save EBX */ movl %ebp, 0x14(%esp) /* save EBP */ - /* store ESP (pointing to context-data) in ECX */ + /* save current context through the first argument pointer */ movl %esp, %ecx - - /* first arg of jump_fcontext() == fcontext to jump to */ movl 0x1c(%esp), %eax + movl %ecx, (%eax) - /* second arg of jump_fcontext() == data to be transferred */ - movl 0x20(%esp), %edx + /* target context and switch data */ + movl 0x20(%esp), %eax + movl 0x24(%esp), %edx - /* restore ESP (pointing to context-data) from EAX */ + /* restore ESP (pointing to context-data) from target */ movl %eax, %esp - /* return parent fcontext_t */ - movl %ecx, %eax - /* returned data is stored in EDX */ - movl 0x18(%esp), %ecx /* restore EIP */ #if !defined(BOOST_USE_TSX) @@ -71,5 +67,8 @@ _jump_fcontext: leal 0x1c(%esp), %esp /* prepare stack */ + /* return and pass data directly */ + movl %edx, %eax + /* jump to context */ jmp *%ecx diff --git a/src/asm/loongarch64/make_loongarch64_sysv_elf_gas.S b/src/asm/loongarch64/make_context_loongarch64_sysv_elf_gas.S similarity index 92% rename from src/asm/loongarch64/make_loongarch64_sysv_elf_gas.S rename to src/asm/loongarch64/make_context_loongarch64_sysv_elf_gas.S index a067bad..98a5f9e 100644 --- a/src/asm/loongarch64/make_loongarch64_sysv_elf_gas.S +++ b/src/asm/loongarch64/make_context_loongarch64_sysv_elf_gas.S @@ -40,18 +40,18 @@ .file "make_loongarch64_sysv_elf_gas.S" .text -.globl make_fcontext -.hidden make_fcontext +.globl yafl_make_context +.hidden yafl_make_context .align 2 -.type make_fcontext,@function -make_fcontext: +.type yafl_make_context,@function +yafl_make_context: # shift address in A0 to lower 16 byte boundary bstrins.d $a0, $zero, 3, 0 # reserve space for context-data on context-stack addi.d $a0, $a0, -160 - # third arg of make_fcontext() == address of context-function + # third arg of yafl_make_context() == address of context-function st.d $a2, $a0, 152 # save address of finish as return-address for context-function @@ -68,6 +68,6 @@ finish: # call _exit(0) b %plt(_exit) -.size make_fcontext, .-make_fcontext +.size yafl_make_context, .-yafl_make_context /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/loongarch64/jump_loongarch64_sysv_elf_gas.S b/src/asm/loongarch64/switch_loongarch64_sysv_elf_gas.S similarity index 89% rename from src/asm/loongarch64/jump_loongarch64_sysv_elf_gas.S rename to src/asm/loongarch64/switch_loongarch64_sysv_elf_gas.S index 6f99e71..7ec0f62 100644 --- a/src/asm/loongarch64/jump_loongarch64_sysv_elf_gas.S +++ b/src/asm/loongarch64/switch_loongarch64_sysv_elf_gas.S @@ -40,11 +40,11 @@ .file "jump_loongarch64_sysv_elf_gas.S" .text -.globl jump_fcontext -.hidden jump_fcontext +.globl yafl_switch +.hidden yafl_switch .align 2 -.type jump_fcontext,@function -jump_fcontext: +.type yafl_switch,@function +yafl_switch: # reserve space on stack addi.d $sp, $sp, -160 @@ -74,11 +74,12 @@ jump_fcontext: # save RA as PC st.d $ra, $sp, 152 - # store SP (pointing to context-data) in A2 - move $a2, $sp + # save current context through the first argument pointer + move $a3, $a2 + st.d $sp, $a0, 0 - # restore SP (pointing to context-data) from A0 - move $sp, $a0 + # restore target context from the second argument + move $sp, $a1 # load fs0 - fs7 fld.d $fs0, $sp, 0 @@ -103,10 +104,8 @@ jump_fcontext: ld.d $fp, $sp, 136 ld.d $ra, $sp, 144 - # return transfer_t from jump - # pass transfer_t as first arg in context function - # a0 == FCTX, a1 == DATA - move $a0, $a2 + # return and pass data directly + move $a0, $a3 # load PC ld.d $a2, $sp, 152 @@ -116,7 +115,7 @@ jump_fcontext: # jump to context jr $a2 -.size jump_fcontext, .-jump_fcontext +.size yafl_switch, .-yafl_switch /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/mips/make_mips32_o32_elf_gas.S b/src/asm/mips/make_context_mips32_o32_elf_gas.S similarity index 85% rename from src/asm/mips/make_mips32_o32_elf_gas.S rename to src/asm/mips/make_context_mips32_o32_elf_gas.S index 54742f3..cdb9757 100644 --- a/src/asm/mips/make_mips32_o32_elf_gas.S +++ b/src/asm/mips/make_context_mips32_o32_elf_gas.S @@ -40,12 +40,12 @@ .file "make_mips32_o32_elf_gas.S" .text -.globl make_fcontext -.hidden make_fcontext +.globl yafl_make_context +.hidden yafl_make_context .align 2 -.type make_fcontext,@function -.ent make_fcontext -make_fcontext: +.type yafl_make_context,@function +.ent yafl_make_context +yafl_make_context: #ifdef __PIC__ .set noreorder .cpload $t9 @@ -57,22 +57,16 @@ make_fcontext: # reserve space for context-data on context-stack # includes an extra 32 bytes for: - # - 16-byte incoming argument area required by mips ABI used when - # jump_context calls the initial function + # - 16-byte incoming argument area required by the MIPS ABI on first entry # - 4 bytes to save our GP register used in finish - # - 8 bytes to as space for transfer_t returned to finish - # - 4 bytes for alignment + # - 12 bytes of reserved scratch/alignment space retained with the original frame layout addiu $v0, $v0, -128 - # third arg of make_fcontext() == address of context-function + # third arg of yafl_make_context() == address of context-function sw $a2, 92($v0) # save global pointer in context-data sw $gp, 112($v0) - # compute address of returned transfer_t - addiu $t0, $v0, 116 - sw $t0, 84($v0) - # compute abs address of label finish la $t9, finish # save address of finish as return-address for context-function @@ -91,8 +85,8 @@ finish: la $t9, _exit move $a0, $zero jr $t9 -.end make_fcontext -.size make_fcontext, .-make_fcontext +.end yafl_make_context +.size yafl_make_context, .-yafl_make_context /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/mips/jump_mips32_o32_elf_gas.S b/src/asm/mips/switch_mips32_o32_elf_gas.S similarity index 84% rename from src/asm/mips/jump_mips32_o32_elf_gas.S rename to src/asm/mips/switch_mips32_o32_elf_gas.S index 38d22d2..cd2e3bb 100644 --- a/src/asm/mips/jump_mips32_o32_elf_gas.S +++ b/src/asm/mips/switch_mips32_o32_elf_gas.S @@ -40,12 +40,12 @@ .file "jump_mips32_o32_elf_gas.S" .text -.globl jump_fcontext -.hidden jump_fcontext +.globl yafl_switch +.hidden yafl_switch .align 2 -.type jump_fcontext,@function -.ent jump_fcontext -jump_fcontext: +.type yafl_switch,@function +.ent yafl_switch +yafl_switch: # reserve space on stack addiu $sp, $sp, -96 @@ -58,7 +58,6 @@ jump_fcontext: sw $s6, 72($sp) # save S6 sw $s7, 76($sp) # save S7 sw $fp, 80($sp) # save FP - sw $a0, 84($sp) # save hidden, address of returned transfer_t sw $ra, 88($sp) # save RA sw $ra, 92($sp) # save RA as PC @@ -71,10 +70,10 @@ jump_fcontext: s.d $f30, 40($sp) # save F30 #endif - # store SP (pointing to context-data) in A0 - move $a0, $sp + # save current context through the first argument pointer + sw $sp, 0($a0) - # restore SP (pointing to context-data) from A1 + # restore target context from the second argument move $sp, $a1 #if defined(__mips_hard_float) @@ -95,7 +94,6 @@ jump_fcontext: lw $s6, 72($sp) # restore S6 lw $s7, 76($sp) # restore S7 lw $fp, 80($sp) # restore FP - lw $v0, 84($sp) # restore hidden, address of returned transfer_t lw $ra, 88($sp) # restore RA # load PC @@ -104,17 +102,14 @@ jump_fcontext: # adjust stack addiu $sp, $sp, 96 - # return transfer_t from jump - sw $a0, ($v0) # fctx of transfer_t - sw $a2, 4($v0) # data of transfer_t - # pass transfer_t as first arg in context function - # A0 == fctx, A1 == data - move $a1, $a2 + # return and pass data directly + move $v0, $a2 + move $a0, $a2 # jump to context jr $t9 -.end jump_fcontext -.size jump_fcontext, .-jump_fcontext +.end yafl_switch +.size yafl_switch, .-yafl_switch /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/mips64/README.md b/src/asm/mips64/README.md index a73c074..9f5ae4f 100644 --- a/src/asm/mips64/README.md +++ b/src/asm/mips64/README.md @@ -6,22 +6,22 @@ This directory contains the MIPS64 N64 ABI context switching implementation for ### API Refactoring (2026) -The original Boost.Context API required fiber functions to explicitly call `jump_fcontext()` at the end to return control. The fcontext library has been refactored to allow fiber functions to return naturally with a `void` return type, eliminating the need for explicit context switches at function end. +The original Boost.Context API required fiber functions to explicitly call `yafl_switch()` at the end to return control. The fcontext library has been refactored to allow fiber functions to return naturally with a `void` return type, eliminating the need for explicit context switches at function end. -This required a key fix in `make_mips64_n64_elf_gas.S`: +This required a key fix in `make_context_mips64_n64_elf_gas.S`: #### Fix in `finish` Routine - Context Pointer Preservation **Original Problem:** Early versions tried to reconstruct the context pointer from $sp using fixed offset calculations (`$sp - 160`). This was unreliable because: -1. `jump_fcontext` adjusts $sp by 160 bytes after restoring the context +1. `yafl_switch` adjusts $sp by 160 bytes after restoring the context 2. The fiber function then allocates its own stack space, moving $sp even further 3. By the time `finish` is reached, $sp is no longer near the context data 4. This caused segmentation faults when fiber functions did substantial work **Solution:** Use a callee-saved register (S1) to preserve the context pointer. -In `make_mips64_n64_elf_gas.S`: +In `make_context_mips64_n64_elf_gas.S`: ```asm # Save context pointer in S1 slot within the context # S1 is callee-saved, so the fiber will preserve it across calls @@ -31,7 +31,7 @@ sd $v0, 72($v0) In `finish` routine: ```asm finish: - # S1 contains the context pointer (saved during make_fcontext and preserved by fiber) + # S1 contains the context pointer (saved during yafl_make_context and preserved by fiber) # S1 is callee-saved, so it survives the entire fiber execution ld $gp, 136($s1) ... @@ -40,7 +40,7 @@ finish: **Why This Works:** - The MIPS64 ABI requires S1 to be callee-saved - We store the context pointer in the S1 register slot at offset 72 during initialization -- When `jump_fcontext` restores the context, it loads S1 from this slot +- When `yafl_switch` restores the context, it loads S1 from this slot - The fiber function must preserve S1 (ABI requirement), so it's still available in `finish` - By using S1 directly, we avoid relying on $sp which has been moved during fiber execution @@ -50,8 +50,8 @@ Stack operations ensure 16-byte alignment compliance required by the MIPS64 ABI. ## Files -- `make_mips64_n64_elf_gas.S` - Context initialization -- `jump_mips64_n64_elf_gas.S` - Context switching/jumping +- `make_context_mips64_n64_elf_gas.S` - Context initialization +- `switch_mips64_n64_elf_gas.S` - Context switching/jumping ## Updating from Boost.Context diff --git a/src/asm/mips64/make_mips64_n64_elf_gas.S b/src/asm/mips64/make_context_mips64_n64_elf_gas.S similarity index 94% rename from src/asm/mips64/make_mips64_n64_elf_gas.S rename to src/asm/mips64/make_context_mips64_n64_elf_gas.S index b7bfd2e..eac6b6f 100644 --- a/src/asm/mips64/make_mips64_n64_elf_gas.S +++ b/src/asm/mips64/make_context_mips64_n64_elf_gas.S @@ -47,12 +47,12 @@ .file "make_mips64_n64_elf_gas.S" .text -.globl make_fcontext -.hidden make_fcontext +.globl yafl_make_context +.hidden yafl_make_context .align 3 -.type make_fcontext,@function -.ent make_fcontext -make_fcontext: +.type yafl_make_context,@function +.ent yafl_make_context +yafl_make_context: #ifdef __PIC__ .set noreorder .cpload $t9 @@ -70,7 +70,7 @@ make_fcontext: # When finish is reached after the fiber returns, S1 will contain the context pointer. sd $v0, 72($v0) - # third arg of make_fcontext() == address of context-function + # third arg of yafl_make_context() == address of context-function sd $a2, 152($v0) # NOTE: $gp (global pointer) is NOT saved here. @@ -99,8 +99,8 @@ finish: dla $t9, _exit move $a0, $zero jr $t9 -.end make_fcontext -.size make_fcontext, .-make_fcontext +.end yafl_make_context +.size yafl_make_context, .-yafl_make_context /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/mips64/jump_mips64_n64_elf_gas.S b/src/asm/mips64/switch_mips64_n64_elf_gas.S similarity index 96% rename from src/asm/mips64/jump_mips64_n64_elf_gas.S rename to src/asm/mips64/switch_mips64_n64_elf_gas.S index 60027fe..13a62ef 100644 --- a/src/asm/mips64/jump_mips64_n64_elf_gas.S +++ b/src/asm/mips64/switch_mips64_n64_elf_gas.S @@ -47,12 +47,12 @@ .file "jump_mips64_n64_elf_gas.S" .text -.globl jump_fcontext -.hidden jump_fcontext +.globl yafl_switch +.hidden yafl_switch .align 3 -.type jump_fcontext,@function -.ent jump_fcontext -jump_fcontext: +.type yafl_switch,@function +.ent yafl_switch +yafl_switch: # reserve space on stack daddiu $sp, $sp, -160 @@ -118,8 +118,8 @@ jump_fcontext: # jump to context jr $t9 -.end jump_fcontext -.size jump_fcontext, .-jump_fcontext +.end yafl_switch +.size yafl_switch, .-yafl_switch /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/ppc32/make_ppc32_sysv_elf_gas.S b/src/asm/ppc32/make_context_ppc32_sysv_elf_gas.S similarity index 81% rename from src/asm/ppc32/make_ppc32_sysv_elf_gas.S rename to src/asm/ppc32/make_context_ppc32_sysv_elf_gas.S index 0dd7771..474cc28 100644 --- a/src/asm/ppc32/make_ppc32_sysv_elf_gas.S +++ b/src/asm/ppc32/make_context_ppc32_sysv_elf_gas.S @@ -52,15 +52,15 @@ .file "make_ppc32_sysv_elf_gas.S" .text -.globl make_fcontext -.hidden make_fcontext +.globl yafl_make_context +.hidden yafl_make_context .align 2 -.type make_fcontext,@function -make_fcontext: +.type yafl_make_context,@function +yafl_make_context: # save return address into R6 mflr %r6 - # first arg of make_fcontext() == top address of context-function + # first arg of yafl_make_context() == top address of context-function # shift address in R3 to lower 16 byte boundary clrrwi %r3, %r3, 4 @@ -68,14 +68,9 @@ make_fcontext: # and parameter area + 240 bytes of context-data (R1 % 16 == 0) subi %r3, %r3, 16 + 240 - # third arg of make_fcontext() == address of context-function -#ifdef __linux__ + # third arg of yafl_make_context() == address of context-function # save context-function as PC stw %r5, 16(%r3) -#else - # save context-function for trampoline - stw %r5, 248(%r3) -#endif # set back-chain to zero li %r0, 0 @@ -85,20 +80,9 @@ make_fcontext: mffs %f0 stfd %f0, 8(%r3) -#ifdef __linux__ - # set hidden pointer for returning transfer_t - la %r0, 248(%r3) - stw %r0, 4(%r3) -#endif - # load address of label 1 into R4 bl 1f 1: mflr %r4 -#ifndef __linux__ - # compute abs address of trampoline, use as PC - addi %r7, %r4, trampoline - 1b - stw %r7, 16(%r3) -#endif # compute abs address of label finish addi %r4, %r4, finish - 1b # save address of finish as return-address for context-function @@ -110,19 +94,6 @@ make_fcontext: blr # return pointer to context-data -#ifndef __linux__ -trampoline: - # On systems other than Linux, jump_fcontext is returning the - # transfer_t in R3:R4, but we need to pass transfer_t * R3 to - # our context-function. - lwz %r0, 8(%r1) # address of context-function - mtctr %r0 - stw %r3, 8(%r1) - stw %r4, 12(%r1) - la %r3, 8(%r1) # address of transfer_t - bctr -#endif - finish: # Use the secure PLT for _exit(0). If we use the insecure BSS PLT # here, then the linker may use the insecure BSS PLT even if the @@ -137,7 +108,7 @@ finish: # call _exit(0) with special addend 0x8000 for large model li %r3, 0 bl _exit + 0x8000@plt -.size make_fcontext, .-make_fcontext +.size yafl_make_context, .-yafl_make_context /* Provide the GOT pointer for secure PLT, large model. */ .section .got2,"aw" diff --git a/src/asm/ppc32/jump_ppc32_sysv_elf_gas.S b/src/asm/ppc32/switch_ppc32_sysv_elf_gas.S similarity index 89% rename from src/asm/ppc32/jump_ppc32_sysv_elf_gas.S rename to src/asm/ppc32/switch_ppc32_sysv_elf_gas.S index e313366..fdb16b1 100644 --- a/src/asm/ppc32/jump_ppc32_sysv_elf_gas.S +++ b/src/asm/ppc32/switch_ppc32_sysv_elf_gas.S @@ -52,13 +52,11 @@ .file "jump_ppc32_sysv_elf_gas.S" .text -.globl jump_fcontext -.hidden jump_fcontext +.globl yafl_switch +.hidden yafl_switch .align 2 -.type jump_fcontext,@function -jump_fcontext: - # Linux: jump_fcontext( hidden transfer_t * R3, R4, R5) - # Other: transfer_t R3:R4 = jump_fcontext( R3, R4) +.type yafl_switch,@function +yafl_switch: mflr %r0 # return address from LR mffs %f0 # FPSCR @@ -67,10 +65,6 @@ jump_fcontext: stwu %r1, -240(%r1) # allocate stack space, R1 % 16 == 0 stw %r0, 244(%r1) # save LR in caller's frame -#ifdef __linux__ - stw %r3, 4(%r1) # hidden pointer -#endif - stfd %f0, 8(%r1) # FPSCR stw %r0, 16(%r1) # LR as PC stw %r8, 20(%r1) # CR @@ -119,16 +113,11 @@ jump_fcontext: stfd %f30, 224(%r1) stfd %f31, 232(%r1) - # store RSP (pointing to context-data) in R7/R6 - # restore RSP (pointing to context-data) from R4/R3 -#ifdef __linux__ - mr %r7, %r1 + # save current context through the first argument pointer + stw %r1, 0(%r3) + + # restore target context from the second argument mr %r1, %r4 - lwz %r3, 4(%r1) # hidden pointer -#else - mr %r6, %r1 - mr %r1, %r3 -#endif lfd %f0, 8(%r1) # FPSCR lwz %r0, 16(%r1) # PC @@ -185,18 +174,12 @@ jump_fcontext: # adjust stack addi %r1, %r1, 240 - # return transfer_t -#ifdef __linux__ - stw %r7, 0(%r3) - stw %r5, 4(%r3) -#else - mr %r3, %r6 - # %r4, %r4 -#endif + # return and pass data directly + mr %r3, %r5 # jump to context bctr -.size jump_fcontext, .-jump_fcontext +.size yafl_switch, .-yafl_switch /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/ppc64/make_ppc64_sysv_elf_gas.S b/src/asm/ppc64/make_context_ppc64_sysv_elf_gas.S similarity index 86% rename from src/asm/ppc64/make_ppc64_sysv_elf_gas.S rename to src/asm/ppc64/make_context_ppc64_sysv_elf_gas.S index f513d36..46a568a 100644 --- a/src/asm/ppc64/make_ppc64_sysv_elf_gas.S +++ b/src/asm/ppc64/make_context_ppc64_sysv_elf_gas.S @@ -67,40 +67,40 @@ *******************************************************/ .file "make_ppc64_sysv_elf_gas.S" -.globl make_fcontext -.hidden make_fcontext +.globl yafl_make_context +.hidden yafl_make_context #if _CALL_ELF == 2 .text .align 2 -make_fcontext: - addis %r2, %r12, .TOC.-make_fcontext@ha - addi %r2, %r2, .TOC.-make_fcontext@l - .localentry make_fcontext, . - make_fcontext +yafl_make_context: + addis %r2, %r12, .TOC.-yafl_make_context@ha + addi %r2, %r2, .TOC.-yafl_make_context@l + .localentry yafl_make_context, . - yafl_make_context #else .section ".opd","aw" .align 3 -make_fcontext: +yafl_make_context: # ifdef _CALL_LINUX - .quad .L.make_fcontext,.TOC.@tocbase,0 - .type make_fcontext,@function + .quad .L.yafl_make_context,.TOC.@tocbase,0 + .type yafl_make_context,@function .text .align 2 -.L.make_fcontext: +.L.yafl_make_context: # else - .hidden .make_fcontext - .globl .make_fcontext - .quad .make_fcontext,.TOC.@tocbase,0 - .size make_fcontext,24 - .type .make_fcontext,@function + .hidden .yafl_make_context + .globl .yafl_make_context + .quad .yafl_make_context,.TOC.@tocbase,0 + .size yafl_make_context,24 + .type .yafl_make_context,@function .text .align 2 -.make_fcontext: +.yafl_make_context: # endif #endif # save return address into R6 mflr %r6 - # first arg of make_fcontext() == top address of context-stack + # first arg of yafl_make_context() == top address of context-stack # shift address in R3 to lower 16 byte boundary clrrdi %r3, %r3, 4 @@ -108,7 +108,7 @@ make_fcontext: # including 64 byte of linkage + parameter area (R1 % 16 == 0) subi %r3, %r3, 248 - # third arg of make_fcontext() == address of context-function + # third arg of yafl_make_context() == address of context-function # entry point (ELFv2) or descriptor (ELFv1) #if _CALL_ELF == 2 # save address of context-function entry point @@ -126,11 +126,6 @@ make_fcontext: li %r0, 0 std %r0, 184(%r3) -#if _CALL_ELF != 2 - # zero in r3 indicates first jump to context-function - std %r0, 152(%r3) -#endif - # load LR mflr %r0 # jump to label 1 @@ -165,12 +160,12 @@ finish: bl _exit nop #if _CALL_ELF == 2 - .size make_fcontext, .-make_fcontext + .size yafl_make_context, .-yafl_make_context #else # ifdef _CALL_LINUX - .size .make_fcontext, .-.L.make_fcontext + .size .yafl_make_context, .-.L.yafl_make_context # else - .size .make_fcontext, .-.make_fcontext + .size .yafl_make_context, .-.yafl_make_context # endif #endif diff --git a/src/asm/ppc64/jump_ppc64_sysv_elf_gas.S b/src/asm/ppc64/switch_ppc64_sysv_elf_gas.S similarity index 81% rename from src/asm/ppc64/jump_ppc64_sysv_elf_gas.S rename to src/asm/ppc64/switch_ppc64_sysv_elf_gas.S index debeb34..722b8de 100644 --- a/src/asm/ppc64/jump_ppc64_sysv_elf_gas.S +++ b/src/asm/ppc64/switch_ppc64_sysv_elf_gas.S @@ -67,34 +67,34 @@ *******************************************************/ .file "jump_ppc64_sysv_elf_gas.S" -.globl jump_fcontext -.hidden jump_fcontext +.globl yafl_switch +.hidden yafl_switch #if _CALL_ELF == 2 .text .align 2 -jump_fcontext: - addis %r2, %r12, .TOC.-jump_fcontext@ha - addi %r2, %r2, .TOC.-jump_fcontext@l - .localentry jump_fcontext, . - jump_fcontext +yafl_switch: + addis %r2, %r12, .TOC.-yafl_switch@ha + addi %r2, %r2, .TOC.-yafl_switch@l + .localentry yafl_switch, . - yafl_switch #else .section ".opd","aw" .align 3 -jump_fcontext: +yafl_switch: # ifdef _CALL_LINUX - .quad .L.jump_fcontext,.TOC.@tocbase,0 - .type jump_fcontext,@function + .quad .L.yafl_switch,.TOC.@tocbase,0 + .type yafl_switch,@function .text .align 2 -.L.jump_fcontext: +.L.yafl_switch: # else - .hidden .jump_fcontext - .globl .jump_fcontext - .quad .jump_fcontext,.TOC.@tocbase,0 - .size jump_fcontext,24 - .type .jump_fcontext,@function + .hidden .yafl_switch + .globl .yafl_switch + .quad .yafl_switch,.TOC.@tocbase,0 + .size yafl_switch,24 + .type .yafl_switch,@function .text .align 2 -.jump_fcontext: +.yafl_switch: # endif #endif # reserve space on stack @@ -121,10 +121,6 @@ jump_fcontext: std %r29, 128(%r1) # save R29 std %r30, 136(%r1) # save R30 std %r31, 144(%r1) # save R31 -#if _CALL_ELF != 2 - std %r3, 152(%r1) # save hidden -#endif - # save CR mfcr %r0 std %r0, 160(%r1) @@ -138,16 +134,13 @@ jump_fcontext: li %r31, 184 stvx %v31, %r1, %r31 - # store RSP (pointing to context-data) in R6 - mr %r6, %r1 + # save current context through the first argument pointer + std %r1, 0(%r3) -#if _CALL_ELF == 2 - # restore RSP (pointing to context-data) from R3 - mr %r1, %r3 -#else - # restore RSP (pointing to context-data) from R4 + # restore target context from the second argument mr %r1, %r4 +#if _CALL_ELF != 2 ld %r2, 0(%r1) # restore TOC #endif @@ -173,10 +166,6 @@ jump_fcontext: ld %r29, 128(%r1) # restore R29 ld %r30, 136(%r1) # restore R30 ld %r31, 144(%r1) # restore R31 -#if _CALL_ELF != 2 - ld %r3, 152(%r1) # restore hidden -#endif - # restore CR ld %r0, 160(%r1) mtcr %r0 @@ -192,37 +181,18 @@ jump_fcontext: # adjust stack addi %r1, %r1, 200 -#if _CALL_ELF == 2 - # copy transfer_t into transfer_fn arg registers - mr %r3, %r6 - # arg pointer already in %r4 + # return and pass data directly + mr %r3, %r5 # jump to context bctr - .size jump_fcontext, .-jump_fcontext +#if _CALL_ELF == 2 + .size yafl_switch, .-yafl_switch #else - # zero in r3 indicates first jump to context-function - cmpdi %r3, 0 - beq use_entry_arg - - # return transfer_t - std %r6, 0(%r3) - std %r5, 8(%r3) - - # jump to context - bctr - -use_entry_arg: - # copy transfer_t into transfer_fn arg registers - mr %r3, %r6 - mr %r4, %r5 - - # jump to context - bctr # ifdef _CALL_LINUX - .size .jump_fcontext, .-.L.jump_fcontext + .size .yafl_switch, .-.L.yafl_switch # else - .size .jump_fcontext, .-.jump_fcontext + .size .yafl_switch, .-.yafl_switch # endif #endif diff --git a/src/asm/riscv64/make_riscv64_sysv_elf_gas.S b/src/asm/riscv64/make_context_riscv64_sysv_elf_gas.S similarity index 94% rename from src/asm/riscv64/make_riscv64_sysv_elf_gas.S rename to src/asm/riscv64/make_context_riscv64_sysv_elf_gas.S index 6d97c59..0811f96 100644 --- a/src/asm/riscv64/make_riscv64_sysv_elf_gas.S +++ b/src/asm/riscv64/make_context_riscv64_sysv_elf_gas.S @@ -60,17 +60,17 @@ .file "make_riscv64_sysv_elf_gas.S" .text .align 1 -.global make_fcontext -.hidden make_fcontext -.type make_fcontext, %function -make_fcontext: +.global yafl_make_context +.hidden yafl_make_context +.type yafl_make_context, %function +yafl_make_context: # shift address in a0 (allocated stack) to lower 16 byte boundary andi a0, a0, ~0xF # reserve space for context-data on context-stack addi a0, a0, -0xd0 - # third arg of make_fcontext() == address of context-function + # third arg of yafl_make_context() == address of context-function # store address as a PC to jump in sd a2, 0xc8(a0) @@ -87,6 +87,6 @@ finish: # exit application tail _exit@plt -.size make_fcontext,.-make_fcontext +.size yafl_make_context,.-yafl_make_context # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/riscv64/jump_riscv64_sysv_elf_gas.S b/src/asm/riscv64/switch_riscv64_sysv_elf_gas.S similarity index 92% rename from src/asm/riscv64/jump_riscv64_sysv_elf_gas.S rename to src/asm/riscv64/switch_riscv64_sysv_elf_gas.S index 879eb7e..fa371a5 100644 --- a/src/asm/riscv64/jump_riscv64_sysv_elf_gas.S +++ b/src/asm/riscv64/switch_riscv64_sysv_elf_gas.S @@ -60,10 +60,10 @@ .file "jump_riscv64_sysv_elf_gas.S" .text .align 1 -.global jump_fcontext -.hidden jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global yafl_switch +.hidden yafl_switch +.type yafl_switch, %function +yafl_switch: # prepare stack for GP + FPU addi sp, sp, -0xd0 @@ -99,11 +99,12 @@ jump_fcontext: # save RA as PC sd ra, 0xc8(sp) - # store SP (pointing to context-data) in A2 - mv a2, sp + # save current context through the first argument pointer + mv a3, a2 + sd sp, 0(a0) - # restore SP (pointing to context-data) from A0 - mv sp, a0 + # restore target context from the second argument + mv sp, a1 # load fs0 - fs11 fld fs0, 0x00(sp) @@ -134,10 +135,8 @@ jump_fcontext: ld s11, 0xb8(sp) ld ra, 0xc0(sp) - # return transfer_t from jump - # pass transfer_t as first arg in context function - # a0 == FCTX, a1 == DATA - mv a0, a2 + # return and pass data directly + mv a0, a3 # load pc ld a2, 0xc8(sp) @@ -146,6 +145,6 @@ jump_fcontext: addi sp, sp, 0xd0 jr a2 -.size jump_fcontext,.-jump_fcontext +.size yafl_switch,.-yafl_switch # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/s390x/make_s390x_sysv_elf_gas.S b/src/asm/s390x/make_context_s390x_sysv_elf_gas.S similarity index 90% rename from src/asm/s390x/make_s390x_sysv_elf_gas.S rename to src/asm/s390x/make_context_s390x_sysv_elf_gas.S index 4d6aa62..b156f9a 100644 --- a/src/asm/s390x/make_s390x_sysv_elf_gas.S +++ b/src/asm/s390x/make_context_s390x_sysv_elf_gas.S @@ -45,9 +45,9 @@ .text .align 8 -.global make_fcontext -.hidden make_fcontext -.type make_fcontext, @function +.global yafl_make_context +.hidden yafl_make_context +.type yafl_make_context, @function #define ARG_OFFSET 0 #define GR_OFFSET 16 @@ -59,7 +59,7 @@ /* -fcontext_t make_fcontext( void * sp, std::size_t size, void (* fn)( transfer_t) ); +fcontext_t yafl_make_context( void * sp, std::size_t size, void (* fn)( void * ) ); Create and return a context below SP to call FN. @@ -70,7 +70,7 @@ r4 - The address of the context function */ -make_fcontext: +yafl_make_context: .machine "z10" /* Align the stack to an 8 byte boundary. */ nill %r2,0xfff8 @@ -78,10 +78,6 @@ make_fcontext: /* Allocate stack space for the context. */ aghi %r2,-CONTEXT_SIZE - /* Set the r2 save slot to zero. This indicates jump_fcontext - that this is a special context. */ - mvghi GR_OFFSET(%r2),0 - /* Save the floating point control register. */ stfpc FPC_OFFSET(%r2) @@ -100,10 +96,10 @@ make_fcontext: finish: /* In finish tasks, you load the exit code and exit the - make_fcontext This is called when the context-function is + yafl_make_context This is called when the context-function is entirely executed. */ lghi %r2,0 brasl %r14,_exit@PLT -.size make_fcontext,.-make_fcontext +.size yafl_make_context,.-yafl_make_context .section .note.GNU-stack,"",%progbits diff --git a/src/asm/s390x/jump_s390x_sysv_elf_gas.S b/src/asm/s390x/switch_s390x_sysv_elf_gas.S similarity index 78% rename from src/asm/s390x/jump_s390x_sysv_elf_gas.S rename to src/asm/s390x/switch_s390x_sysv_elf_gas.S index 59bc58b..b1e587a 100644 --- a/src/asm/s390x/jump_s390x_sysv_elf_gas.S +++ b/src/asm/s390x/switch_s390x_sysv_elf_gas.S @@ -45,9 +45,9 @@ .text .align 8 -.global jump_fcontext -.hidden jump_fcontext -.type jump_fcontext, @function +.global yafl_switch +.hidden yafl_switch +.type yafl_switch, @function #define ARG_OFFSET 0 #define GR_OFFSET 16 @@ -63,29 +63,22 @@ typedef void* fcontext_t; -struct transfer_t { - fcontext_t fctx; - void * data; -}; - -transfer_t jump_fcontext( fcontext_t const to, - void * data); +void * yafl_switch( fcontext_t * save, + fcontext_t target, + void * data); Incoming args -r2 - Hidden argument to the location where the return transfer_t needs to be returned +r2 - Pointer where the current context will be saved r3 - Context we want to switch to r4 - Data pointer */ -jump_fcontext: +yafl_switch: .machine "z10" /* Reserve stack space to store the current context. */ aghi %r15,-CONTEXT_SIZE - /* Save the argument register holding the location of the return value. */ - stg %r2,GR_OFFSET(%r15) - /* Save the call-saved general purpose registers. */ stmg %r6,%r14,GR_OFFSET+8(%r15) @@ -105,8 +98,8 @@ jump_fcontext: /* Save the floating point control register. */ stfpc FPC_OFFSET(%r15) - /* Backup the stack pointer pointing to the old context-data into r1. */ - lgr %r1,%r15 + /* Save the current context through the first argument pointer. */ + stg %r15,0(%r2) /* Load the new context pointer as stack pointer. */ lgr %r15,%r3 @@ -130,32 +123,15 @@ jump_fcontext: /* Restore PC - the location where we will jump to at the end. */ lg %r5,PC_OFFSET(%r15) - ltg %r2,GR_OFFSET(%r15) - jnz use_return_slot - - /* We're restoring a context created by make_fcontext. - This is going to be the argument of the entry point - of the fiber. We're placing it on top of the ABI - defined register save area of the fiber's own stack. */ - la %r2,REG_SAVE_AREA_SIZE(%r15) - - /* REG_SAVE_AREA_SIZE + sizeof(transfer_t) */ - aghi %r15,-(REG_SAVE_AREA_SIZE+16) - -use_return_slot: - /* Save the two fields in transfer_t. When calling a - make_fcontext function this becomes the function argument of - the target function, otherwise it will be the return value of - jump_fcontext. */ - stg %r1,0(%r2) - stg %r4,8(%r2) - /* Free the restored context. */ aghi %r15,CONTEXT_SIZE + /* Return and pass data directly. */ + lgr %r2,%r4 + /* Jump to the PC loaded from the new context. */ br %r5 -.size jump_fcontext,.-jump_fcontext +.size yafl_switch,.-yafl_switch .section .note.GNU-stack,"",%progbits diff --git a/src/asm/sparc64/make_sparc64_sysv_elf_gas.S b/src/asm/sparc64/make_context_sparc64_sysv_elf_gas.S similarity index 81% rename from src/asm/sparc64/make_sparc64_sysv_elf_gas.S rename to src/asm/sparc64/make_context_sparc64_sysv_elf_gas.S index 3d50447..8cc0cf6 100644 --- a/src/asm/sparc64/make_sparc64_sysv_elf_gas.S +++ b/src/asm/sparc64/make_context_sparc64_sysv_elf_gas.S @@ -6,7 +6,7 @@ */ /* - * fcontext_t *make_fcontext(void *sp, size_t size, void (*fn)(transfer_t)); + * fcontext_t *yafl_make_context(void *sp, size_t size, void (*fn)(void *)); */ #define CC64FSZ 176 #define BIAS 2047 @@ -17,9 +17,9 @@ .file "make_sparc64_sysv_elf_gas.S" .text .align 4 -.global make_fcontext -.type make_fcontext, %function -make_fcontext: +.global yafl_make_context +.type yafl_make_context, %function +yafl_make_context: save %sp, -CC64FSZ, %sp # shift address in %i0 (allocated stack) to lower 16 byte boundary @@ -27,10 +27,10 @@ make_fcontext: # reserve space for two frames on the stack # the first frame is for the call the second one holds the data - # for jump_fcontext + # for yafl_switch sub %i0, 2 * CC64FSZ, %i0 - # third argument of make_fcontext() is the context-function to call + # third argument of yafl_make_context() is the context-function to call # store it in the first stack frame, also clear %fp there to indicate # the end of the stack. stx %i2, [%i0 + CC64FSZ + I7] @@ -56,13 +56,13 @@ make_fcontext: trampoline: ldx [%sp + BIAS + I7], %l0 - # no need to setup transfer_t, already in %o0 and %o1 + # the first-entry argument already arrives in %o0 jmpl %l0, %o7 nop call _exit clr %o0 unimp -.size make_fcontext,.-make_fcontext +.size yafl_make_context,.-yafl_make_context # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/sparc64/jump_sparc64_sysv_elf_gas.S b/src/asm/sparc64/switch_sparc64_sysv_elf_gas.S similarity index 65% rename from src/asm/sparc64/jump_sparc64_sysv_elf_gas.S rename to src/asm/sparc64/switch_sparc64_sysv_elf_gas.S index 61101fb..3f47c21 100644 --- a/src/asm/sparc64/jump_sparc64_sysv_elf_gas.S +++ b/src/asm/sparc64/switch_sparc64_sysv_elf_gas.S @@ -8,12 +8,7 @@ /* * typedef void* fcontext_t; * - * struct transfer_t { - * fcontext_t fctx; - * void * data; - * }; - * - * transfer_t jump_fcontext(fcontext_t const to, void *vp); + * void *yafl_switch(fcontext_t *save, fcontext_t target, void *data); */ #define CC64FSZ 176 #define BIAS 2047 @@ -23,9 +18,9 @@ .file "jump_sparc64_sysv_elf_gas.S" .text .align 4 -.global jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global yafl_switch +.type yafl_switch, %function +yafl_switch: # prepare stack save %sp, -CC64FSZ, %sp @@ -33,19 +28,19 @@ jump_fcontext: # for arguments stx %fp, [%sp + BIAS + SP] stx %i7, [%sp + BIAS + I7] - mov %sp, %o0 + mov %sp, %l0 # force flush register windows to stack and with that save context flushw - # get SP (pointing to new context-data) from %i0 param - mov %i0, %sp + # save current context through the first argument pointer + stx %l0, [%i0] + # get SP (pointing to new context-data) from %i1 param + mov %i1, %sp # load framepointer and return address from context ldx [%sp + BIAS + SP], %fp ldx [%sp + BIAS + I7], %i7 ret - restore %o0, %g0, %o0 - # restore old %sp (pointing to old context-data) in %o0 - # *data stored in %o1 was not modified -.size jump_fcontext,.-jump_fcontext + restore %i2, %g0, %o0 +.size yafl_switch,.-yafl_switch # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/src/asm/x86_64/make_x86_64_ms_pe_gas.S b/src/asm/x86_64/make_context_x86_64_ms_pe_gas.S similarity index 93% rename from src/asm/x86_64/make_x86_64_ms_pe_gas.S rename to src/asm/x86_64/make_context_x86_64_ms_pe_gas.S index e854c91..370e5b4 100644 --- a/src/asm/x86_64/make_x86_64_ms_pe_gas.S +++ b/src/asm/x86_64/make_context_x86_64_ms_pe_gas.S @@ -90,13 +90,13 @@ .file "make_x86_64_ms_pe_gas.S" .text .p2align 4,,15 -.globl make_fcontext -.def make_fcontext; .scl 2; .type 32; .endef -.seh_proc make_fcontext -make_fcontext: +.globl yafl_make_context +.def yafl_make_context; .scl 2; .type 32; .endef +.seh_proc yafl_make_context +yafl_make_context: .seh_endprologue - /* first arg of make_fcontext() == top of context-stack */ + /* first arg of yafl_make_context() == top of context-stack */ movq %rcx, %rax /* shift address in RAX to lower 16 byte boundary */ @@ -107,13 +107,13 @@ make_fcontext: /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x150(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of yafl_make_context() == address of context-function */ movq %r8, 0x100(%rax) - /* first arg of make_fcontext() == top of context-stack */ + /* first arg of yafl_make_context() == top of context-stack */ /* save top address of context stack as 'base' */ movq %rcx, 0xc8(%rax) - /* second arg of make_fcontext() == size of context-stack */ + /* second arg of yafl_make_context() == size of context-stack */ /* negate stack size for LEA instruction (== subtraction) */ negq %rdx /* compute bottom address of context stack (limit) */ @@ -131,15 +131,10 @@ make_fcontext: /* save x87 control-word */ fnstcw 0xa4(%rax) - /* compute address of transport_t */ - leaq 0x140(%rax), %rcx - /* store address of transport_t in hidden field */ - movq %rcx, 0x110(%rax) - /* compute abs address of label trampoline */ leaq trampoline(%rip), %rcx /* save address of finish as return-address for context-function */ - /* will be entered after jump_fcontext() first time */ + /* will be entered after yafl_switch() first time */ movq %rcx, 0x118(%rax) /* compute abs address of label finish */ @@ -161,7 +156,7 @@ finish: /* 32byte shadow-space for _exit() */ andq $-32, %rsp /* 32byte shadow-space for _exit() are */ - /* already reserved by make_fcontext() */ + /* already reserved by yafl_make_context() */ /* exit code is zero */ xorq %rcx, %rcx /* exit application */ @@ -172,4 +167,4 @@ finish: .def _exit; .scl 2; .type 32; .endef /* standard C library function */ .section .drectve -.ascii " -export:\"make_fcontext\"" +.ascii " -export:\"yafl_make_context\"" diff --git a/src/asm/x86_64/make_x86_64_ms_pe_masm.asm b/src/asm/x86_64/make_context_x86_64_ms_pe_masm.asm similarity index 94% rename from src/asm/x86_64/make_x86_64_ms_pe_masm.asm rename to src/asm/x86_64/make_context_x86_64_ms_pe_masm.asm index 408f735..09a5685 100644 --- a/src/asm/x86_64/make_x86_64_ms_pe_masm.asm +++ b/src/asm/x86_64/make_context_x86_64_ms_pe_masm.asm @@ -92,11 +92,11 @@ EXTERN _exit:PROC .code ; generate function table entry in .pdata and unwind information in -make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME +yafl_make_context PROC BOOST_CONTEXT_EXPORT FRAME ; .xdata for a function's structured exception handling unwind behavior .endprolog - ; first arg of make_fcontext() == top of context-stack + ; first arg of yafl_make_context() == top of context-stack mov rax, rcx ; shift address in RAX to lower 16 byte boundary @@ -107,14 +107,14 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME ; on context-function entry: (RSP -0x8) % 16 == 0 sub rax, 0150h - ; third arg of make_fcontext() == address of context-function + ; third arg of yafl_make_context() == address of context-function ; stored in RBX mov [rax+0100h], r8 - ; first arg of make_fcontext() == top of context-stack + ; first arg of yafl_make_context() == top of context-stack ; save top address of context stack as 'base' mov [rax+0c8h], rcx - ; second arg of make_fcontext() == size of context-stack + ; second arg of yafl_make_context() == size of context-stack ; negate stack size for LEA instruction (== subtraction) neg rdx ; compute bottom address of context stack (limit) @@ -132,15 +132,10 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME ; save x87 control-word fnstcw [rax+0a4h] - ; compute address of transport_t - lea rcx, [rax+0140h] - ; store address of transport_t in hidden field - mov [rax+0110h], rcx - ; compute abs address of label trampoline lea rcx, trampoline ; save address of trampoline as return-address for context-function - ; will be entered after calling jump_fcontext() first time + ; will be entered after calling yafl_switch() first time mov [rax+0118h], rcx ; compute abs address of label finish @@ -164,5 +159,5 @@ finish: ; exit application call _exit hlt -make_fcontext ENDP +yafl_make_context ENDP END diff --git a/src/asm/x86_64/make_x86_64_sysv_elf_gas.S b/src/asm/x86_64/make_context_x86_64_sysv_elf_gas.S similarity index 94% rename from src/asm/x86_64/make_x86_64_sysv_elf_gas.S rename to src/asm/x86_64/make_context_x86_64_sysv_elf_gas.S index cabebff..df5e2a6 100644 --- a/src/asm/x86_64/make_x86_64_sysv_elf_gas.S +++ b/src/asm/x86_64/make_context_x86_64_sysv_elf_gas.S @@ -43,11 +43,11 @@ # endif .file "make_x86_64_sysv_elf_gas.S" .text -.globl make_fcontext -.hidden make_fcontext -.type make_fcontext,@function +.globl yafl_make_context +.hidden yafl_make_context +.type yafl_make_context,@function .align 16 -make_fcontext: +yafl_make_context: _CET_ENDBR #if BOOST_CONTEXT_SHADOW_STACK @@ -55,7 +55,7 @@ make_fcontext: movq -0x8(%rdi), %r9 #endif - /* first arg of make_fcontext() == top of context-stack */ + /* first arg of yafl_make_context() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ @@ -65,7 +65,7 @@ make_fcontext: /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of yafl_make_context() == address of context-function */ /* stored in RBX */ movq %rdx, 0x30(%rax) @@ -83,7 +83,7 @@ make_fcontext: /* compute abs address of label trampoline */ leaq trampoline(%rip), %rcx /* save address of trampoline as return-address for context-function */ - /* will be entered after calling jump_fcontext() first time */ + /* will be entered after calling yafl_switch() first time */ movq %rcx, 0x40(%rax) /* compute abs address of label finish */ @@ -158,7 +158,7 @@ finish: /* exit application */ call _exit@PLT hlt -.size make_fcontext,.-make_fcontext +.size yafl_make_context,.-yafl_make_context /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/x86_64/make_x86_64_sysv_macho_gas.S b/src/asm/x86_64/make_context_x86_64_sysv_macho_gas.S similarity index 91% rename from src/asm/x86_64/make_x86_64_sysv_macho_gas.S rename to src/asm/x86_64/make_context_x86_64_sysv_macho_gas.S index 06357f6..113b853 100644 --- a/src/asm/x86_64/make_x86_64_sysv_macho_gas.S +++ b/src/asm/x86_64/make_context_x86_64_sysv_macho_gas.S @@ -25,11 +25,11 @@ ****************************************************************************************/ .text -.private_extern _make_fcontext -.globl _make_fcontext +.private_extern _yafl_make_context +.globl _yafl_make_context .align 8 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +_yafl_make_context: + /* first arg of yafl_make_context() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ @@ -39,7 +39,7 @@ _make_fcontext: /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x40(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ + /* third arg of yafl_make_context() == address of context-function */ /* stored in RBX */ movq %rdx, 0x28(%rax) @@ -51,7 +51,7 @@ _make_fcontext: /* compute abs address of label trampoline */ leaq trampoline(%rip), %rcx /* save address of trampoline as return-address for context-function */ - /* will be entered after calling jump_fcontext() first time */ + /* will be entered after calling yafl_switch() first time */ movq %rcx, 0x38(%rax) /* compute abs address of label finish */ diff --git a/src/asm/x86_64/jump_x86_64_ms_pe_gas.S b/src/asm/x86_64/switch_x86_64_ms_pe_gas.S similarity index 83% rename from src/asm/x86_64/jump_x86_64_ms_pe_gas.S rename to src/asm/x86_64/switch_x86_64_ms_pe_gas.S index 6507c1e..0a96d46 100644 --- a/src/asm/x86_64/jump_x86_64_ms_pe_gas.S +++ b/src/asm/x86_64/switch_x86_64_ms_pe_gas.S @@ -10,10 +10,10 @@ .file "jump_x86_64_ms_pe_gas.S" .text .p2align 4,,15 -.globl jump_fcontext -.def jump_fcontext; .scl 2; .type 32; .endef -.seh_proc jump_fcontext -jump_fcontext: +.globl yafl_switch +.def yafl_switch; .scl 2; .type 32; .endef +.seh_proc yafl_switch +yafl_switch: .seh_endprologue leaq -0x118(%rsp), %rsp /* prepare stack */ @@ -58,12 +58,12 @@ jump_fcontext: movq %rbx, 0x100(%rsp) /* save RBX */ movq %rbp, 0x108(%rsp) /* save RBP */ - movq %rcx, 0x110(%rsp) /* save hidden address of transport_t */ + /* save current context through the first argument pointer */ + movq %r8, %r9 + movq %rsp, %rax + movq %rax, (%rcx) - /* preserve RSP (pointing to context-data) in R9 */ - movq %rsp, %r9 - - /* restore RSP (pointing to context-data) from RDX */ + /* restore target context from the second argument */ movq %rdx, %rsp #if !defined(BOOST_USE_TSX) @@ -106,25 +106,18 @@ jump_fcontext: movq 0x100(%rsp), %rbx /* restore RBX */ movq 0x108(%rsp), %rbp /* restore RBP */ - movq 0x110(%rsp), %rax /* restore hidden address of transport_t */ - leaq 0x118(%rsp), %rsp /* prepare stack */ /* restore return-address */ popq %r10 - /* transport_t returned in RAX */ - /* return parent fcontext_t */ - movq %r9, 0x0(%rax) - /* return data */ - movq %r8, 0x8(%rax) - - /* transport_t as 1.arg of context-function */ - movq %rax, %rcx + /* return and pass data directly */ + movq %r9, %rax + movq %r9, %rcx /* indirect jump to context */ jmp *%r10 .seh_endproc .section .drectve -.ascii " -export:\"jump_fcontext\"" +.ascii " -export:\"yafl_switch\"" diff --git a/src/asm/x86_64/jump_x86_64_ms_pe_masm.asm b/src/asm/x86_64/switch_x86_64_ms_pe_masm.asm similarity index 94% rename from src/asm/x86_64/jump_x86_64_ms_pe_masm.asm rename to src/asm/x86_64/switch_x86_64_ms_pe_masm.asm index 4d3980d..3dfaa87 100644 --- a/src/asm/x86_64/jump_x86_64_ms_pe_masm.asm +++ b/src/asm/x86_64/switch_x86_64_ms_pe_masm.asm @@ -89,7 +89,7 @@ ENDIF .code -jump_fcontext PROC BOOST_CONTEXT_EXPORT FRAME +yafl_switch PROC BOOST_CONTEXT_EXPORT FRAME .endprolog ; prepare stack @@ -137,12 +137,12 @@ ENDIF mov [rsp+0100h], rbx ; save RBX mov [rsp+0108h], rbp ; save RBP - mov [rsp+0110h], rcx ; save hidden address of transport_t + ; save current context through the first argument pointer + mov r9, r8 + mov rax, rsp + mov [rcx], rax - ; preserve RSP (pointing to context-data) in R9 - mov r9, rsp - - ; restore RSP (pointing to context-data) from RDX + ; restore target context from the second argument mov rsp, rdx IFNDEF BOOST_USE_TSX @@ -187,24 +187,17 @@ ENDIF mov rbx, [rsp+0100h] ; restore RBX mov rbp, [rsp+0108h] ; restore RBP - mov rax, [rsp+0110h] ; restore hidden address of transport_t - ; prepare stack lea rsp, [rsp+0118h] ; load return-address pop r10 - ; transport_t returned in RAX - ; return parent fcontext_t - mov [rax], r9 - ; return data - mov [rax+08h], r8 - - ; transport_t as 1.arg of context-function - mov rcx, rax + ; return and pass data directly + mov rax, r9 + mov rcx, r9 ; indirect jump to context jmp r10 -jump_fcontext ENDP +yafl_switch ENDP END diff --git a/src/asm/x86_64/jump_x86_64_sysv_elf_gas.S b/src/asm/x86_64/switch_x86_64_sysv_elf_gas.S similarity index 87% rename from src/asm/x86_64/jump_x86_64_sysv_elf_gas.S rename to src/asm/x86_64/switch_x86_64_sysv_elf_gas.S index 2eff59a..03ba81e 100644 --- a/src/asm/x86_64/jump_x86_64_sysv_elf_gas.S +++ b/src/asm/x86_64/switch_x86_64_sysv_elf_gas.S @@ -43,11 +43,11 @@ # endif .file "jump_x86_64_sysv_elf_gas.S" .text -.globl jump_fcontext -.hidden jump_fcontext -.type jump_fcontext,@function +.globl yafl_switch +.hidden yafl_switch +.type yafl_switch,@function .align 16 -jump_fcontext: +yafl_switch: _CET_ENDBR leaq -0x40(%rsp), %rsp /* prepare stack */ @@ -76,11 +76,13 @@ jump_fcontext: movq %rcx, (%rsp) # endif - /* store RSP (pointing to context-data) in RAX */ + /* save current context through the first argument pointer */ + movq %rdx, %r9 movq %rsp, %rax + movq %rax, (%rdi) - /* restore RSP (pointing to context-data) from RDI */ - movq %rdi, %rsp + /* restore target context from the second argument */ + movq %rsi, %rsp #if BOOST_CONTEXT_SHADOW_STACK /* first 8 bytes are SSP */ @@ -93,7 +95,7 @@ jump_fcontext: /* on previous shadow stack after saveprevssp */ saveprevssp - /* when return, jump_fcontext jump to restored return address */ + /* when return, yafl_switch jump to restored return address */ /* (r8) instead of RET. This miss of RET implies us to unwind */ /* shadow stack accordingly. Otherwise mismatch occur */ movq $1, %rcx @@ -121,26 +123,13 @@ jump_fcontext: leaq 0x48(%rsp), %rsp /* prepare stack */ - /* return transfer_t from jump */ -#if !defined(_ILP32) - /* RAX == fctx, RDX == data */ - movq %rsi, %rdx -#else - /* RAX == data:fctx */ - salq $32, %rsi - orq %rsi, %rax -#endif - /* pass transfer_t as first arg in context function */ -#if !defined(_ILP32) - /* RDI == fctx, RSI == data */ -#else - /* RDI == data:fctx */ -#endif - movq %rax, %rdi + /* return and pass data directly */ + movq %r9, %rax + movq %r9, %rdi /* indirect jump to context */ jmp *%r8 -.size jump_fcontext,.-jump_fcontext +.size yafl_switch,.-yafl_switch /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/src/asm/x86_64/jump_x86_64_sysv_macho_gas.S b/src/asm/x86_64/switch_x86_64_sysv_macho_gas.S similarity index 87% rename from src/asm/x86_64/jump_x86_64_sysv_macho_gas.S rename to src/asm/x86_64/switch_x86_64_sysv_macho_gas.S index 673daa6..cc41330 100644 --- a/src/asm/x86_64/jump_x86_64_sysv_macho_gas.S +++ b/src/asm/x86_64/switch_x86_64_sysv_macho_gas.S @@ -25,10 +25,10 @@ ****************************************************************************************/ .text -.private_extern _jump_fcontext -.globl _jump_fcontext +.private_extern _yafl_switch +.globl _yafl_switch .align 8 -_jump_fcontext: +_yafl_switch: leaq -0x38(%rsp), %rsp /* prepare stack */ #if !defined(BOOST_USE_TSX) @@ -43,11 +43,13 @@ _jump_fcontext: movq %rbx, 0x28(%rsp) /* save RBX */ movq %rbp, 0x30(%rsp) /* save RBP */ - /* store RSP (pointing to context-data) in RAX */ + /* save current context through the first argument pointer */ + movq %rdx, %r9 movq %rsp, %rax + movq %rax, (%rdi) - /* restore RSP (pointing to context-data) from RDI */ - movq %rdi, %rsp + /* restore target context from the second argument */ + movq %rsi, %rsp movq 0x38(%rsp), %r8 /* restore return-address */ @@ -65,12 +67,9 @@ _jump_fcontext: leaq 0x40(%rsp), %rsp /* prepare stack */ - /* return transfer_t from jump */ - /* RAX == fctx, RDX == data */ - movq %rsi, %rdx - /* pass transfer_t as first arg in context function */ - /* RDI == fctx, RSI == data */ - movq %rax, %rdi + /* return and pass data directly */ + movq %r9, %rax + movq %r9, %rdi /* indirect jump to context */ jmp *%r8 diff --git a/src/yafl.c b/src/yafl.c index 96e0cbb..7534080 100644 --- a/src/yafl.c +++ b/src/yafl.c @@ -33,18 +33,13 @@ /* Raw context handle - opaque pointer to saved machine state */ typedef struct yafl_opaque_t *yafl_t; -/* Raw transfer between contexts */ -typedef struct { - yafl_t prev_context; - void *data; -} yafl_transfer_t; - /* Raw entry function type for low-level API */ -typedef void (*yafl_entry_t)(yafl_transfer_t); +typedef void (*yafl_entry_t)(void *); /* Low-level assembly-implemented functions */ -extern yafl_t make_fcontext(void *sp, size_t size, yafl_entry_t fn); -extern yafl_transfer_t jump_fcontext(yafl_t const to, void *vp); +/* Creates the initial saved context for a fiber stack. */ +extern yafl_t yafl_make_context(void *sp, size_t size, yafl_entry_t fn); +extern void *yafl_switch(yafl_t *save, yafl_t target, void *data); /* ======================================================================== * Constants and Types @@ -76,7 +71,6 @@ struct yafl_fiber { /* User entry and result */ yafl_fiber_fn user_entry; void *cached_result; - void *pending_arg; /* Argument for next resume */ }; /* Typedef for internal use */ @@ -85,6 +79,37 @@ typedef struct yafl_fiber yafl_fiber_t; /* Thread-local storage */ static _Thread_local yafl_fiber_t *tls_current_fiber = NULL; +static void fiber_entry_trampoline(void *arg); + +static void free_fiber_stack(yafl_fiber_t *fiber) { + if(fiber == NULL || fiber->stack_region == NULL) { return; } + + if(fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { + free(fiber->stack_region); + } else if(fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { +#ifdef _WIN32 + VirtualFree(fiber->stack_region, 0, MEM_RELEASE); +#else + munmap(fiber->stack_region, fiber->stack_total_size); +#endif + } + + fiber->stack_region = NULL; + fiber->stack_total_size = 0; + fiber->stack_top = NULL; + fiber->stack_size = 0; +} + +static bool initialize_fiber_context(yafl_fiber_t *fiber) { + fiber->context = yafl_make_context(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); + return fiber->context != NULL; +} + +static bool reinitialize_fiber_context_with_watermark(yafl_fiber_t *fiber) { + memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); + return initialize_fiber_context(fiber); +} + /* ======================================================================== * Utility Functions * ======================================================================== */ @@ -111,21 +136,19 @@ static void *align_stack_pointer(void *ptr) { * ======================================================================== */ /* - * This is called as the entry function by make_fcontext(). + * This is called as the entry function by yafl_make_context(). * It wraps the user's entry function, manages state, and handles the result. */ -static void fiber_entry_trampoline(yafl_transfer_t t) { - yafl_fiber_t *fiber = (yafl_fiber_t *)t.data; - - /* Save resumer's context (who called resume on us) */ - fiber->resumer_context = t.prev_context; +static void fiber_entry_trampoline(void *arg) { + yafl_fiber_t *fiber = tls_current_fiber; + if(fiber == NULL) { abort(); } /* Update status and TLS */ fiber->status = YAFL_FIBER_STATUS_RUNNING; tls_current_fiber = fiber; /* Call user entry with argument from first resume */ - void *result = fiber->user_entry(fiber->pending_arg); + void *result = fiber->user_entry(arg); /* Mark complete and cache result */ fiber->status = YAFL_FIBER_STATUS_COMPLETE; @@ -133,9 +156,10 @@ static void fiber_entry_trampoline(yafl_transfer_t t) { tls_current_fiber = NULL; /* Return to resumer with final result */ - jump_fcontext(fiber->resumer_context, result); + yafl_switch(&fiber->context, fiber->resumer_context, result); /* Should never reach here */ + abort(); } /* ======================================================================== @@ -163,10 +187,13 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size fiber->status = YAFL_FIBER_STATUS_SUSPENDED; fiber->user_entry = fiber_fn; fiber->cached_result = NULL; - fiber->pending_arg = NULL; fiber->watermark_filled = use_watermark; fiber->context = NULL; fiber->resumer_context = NULL; + fiber->stack_region = NULL; + fiber->stack_total_size = 0; + fiber->stack_top = NULL; + fiber->stack_size = 0; /* Use default stack size if not specified */ if(stack_size == 0) { stack_size = FCONTEXT_DEFAULT_STACK_SIZE; } @@ -182,32 +209,18 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size void *region = NULL; #ifdef _WIN32 region = VirtualAlloc(NULL, total_size, MEM_RESERVE, PAGE_NOACCESS); - if(region == NULL) { - free(fiber); - return NULL; - } + if(region == NULL) { goto create_fail; } + fiber->stack_region = region; + fiber->stack_total_size = total_size; void *stack_base = (char *)region + guard_size; - if(!VirtualAlloc(stack_base, aligned_stack_size, MEM_COMMIT, PAGE_READWRITE)) { - VirtualFree(region, 0, MEM_RELEASE); - free(fiber); - return NULL; - } + if(!VirtualAlloc(stack_base, aligned_stack_size, MEM_COMMIT, PAGE_READWRITE)) { goto create_fail; } #else region = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if(region == MAP_FAILED) { - free(fiber); - return NULL; - } - if(mprotect(region, guard_size, PROT_NONE) == -1) { - munmap(region, total_size); - free(fiber); - return NULL; - } - if(mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { - munmap(region, total_size); - free(fiber); - return NULL; - } + if(region == MAP_FAILED) { goto create_fail; } + fiber->stack_region = region; + fiber->stack_total_size = total_size; + if(mprotect(region, guard_size, PROT_NONE) == -1) { goto create_fail; } + if(mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { goto create_fail; } void *stack_base = (char *)region + guard_size; #endif @@ -216,18 +229,15 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size stack_top = align_stack_pointer(stack_top); size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)stack_base); - fiber->stack_region = region; - fiber->stack_total_size = total_size; fiber->stack_top = stack_top; fiber->stack_size = actual_stack_size; } else { /* malloc allocation */ size_t allocated_size = stack_size + 256; void *block = malloc(allocated_size); - if(block == NULL) { - free(fiber); - return NULL; - } + if(block == NULL) { goto create_fail; } + fiber->stack_region = block; + fiber->stack_total_size = allocated_size; void *block_end = (char *)block + allocated_size; void *stack_top = (char *)block_end - 256; @@ -235,49 +245,24 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)block); - fiber->stack_region = block; - fiber->stack_total_size = allocated_size; fiber->stack_top = stack_top; fiber->stack_size = actual_stack_size; } /* Initialize the low-level context with trampoline as entry */ - fiber->context = make_fcontext(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); - if(fiber->context == NULL) { - if(use_vmem) { -#ifdef _WIN32 - VirtualFree(fiber->stack_region, 0, MEM_RELEASE); -#else - munmap(fiber->stack_region, fiber->stack_total_size); -#endif - } else { - free(fiber->stack_region); - } - free(fiber); - return NULL; - } + if(!initialize_fiber_context(fiber)) { goto create_fail; } /* Apply watermark if requested */ if(use_watermark) { - memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); - /* Reinitialize context after watermark (overwrites filled area) */ - fiber->context = make_fcontext(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); - if(fiber->context == NULL) { - if(use_vmem) { -#ifdef _WIN32 - VirtualFree(fiber->stack_region, 0, MEM_RELEASE); -#else - munmap(fiber->stack_region, fiber->stack_total_size); -#endif - } else { - free(fiber->stack_region); - } - free(fiber); - return NULL; - } + if(!reinitialize_fiber_context_with_watermark(fiber)) { goto create_fail; } } return fiber; + +create_fail: + free_fiber_stack(fiber); + free(fiber); + return NULL; } /* ======================================================================== @@ -294,24 +279,17 @@ extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { /* Cannot resume running fiber */ if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return NULL; } - /* Store argument for delivery */ - fiber->pending_arg = arg; - /* Update status and TLS */ fiber->status = YAFL_FIBER_STATUS_RUNNING; tls_current_fiber = fiber; - /* Pass fiber pointer to trampoline on first resume */ - void *transfer_data = (void *)fiber; - /* Perform context switch */ - yafl_transfer_t t = jump_fcontext(fiber->context, transfer_data); + void *result = yafl_switch(&fiber->resumer_context, fiber->context, arg); - /* Back in resumer - update fiber's context for next resume */ - fiber->context = t.prev_context; + /* Back in resumer */ tls_current_fiber = NULL; - return t.data; + return result; } extern void *yafl_fiber_suspend(void *result) { @@ -324,15 +302,14 @@ extern void *yafl_fiber_suspend(void *result) { current->status = YAFL_FIBER_STATUS_SUSPENDED; /* Switch back to resumer */ - yafl_transfer_t t = jump_fcontext(current->resumer_context, result); + void *arg = yafl_switch(¤t->context, current->resumer_context, result); /* When resumed - restore state */ current->status = YAFL_FIBER_STATUS_RUNNING; - current->resumer_context = t.prev_context; tls_current_fiber = current; /* Return argument passed to resume */ - return current->pending_arg; + return arg; } /* ======================================================================== @@ -366,16 +343,7 @@ extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { /* Cannot destroy running fiber */ if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return; } - /* Free stack based on allocation type */ - if(fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { - free(fiber->stack_region); - } else if(fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { -#ifdef _WIN32 - VirtualFree(fiber->stack_region, 0, MEM_RELEASE); -#else - munmap(fiber->stack_region, fiber->stack_total_size); -#endif - } + free_fiber_stack(fiber); /* Invalidate and free */ fiber->magic = 0; diff --git a/toolchains/aarch64-apple-darwin.cmake b/toolchains/aarch64-apple-darwin.cmake index 520d885..749e900 100644 --- a/toolchains/aarch64-apple-darwin.cmake +++ b/toolchains/aarch64-apple-darwin.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/arm64/make_arm64_aapcs_macho_gas.S - src/asm/arm64/jump_arm64_aapcs_macho_gas.S + src/asm/arm64/make_context_arm64_aapcs_macho_gas.S + src/asm/arm64/switch_arm64_aapcs_macho_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/aarch64-apple-ios.cmake b/toolchains/aarch64-apple-ios.cmake index b59ccb3..3cd9961 100644 --- a/toolchains/aarch64-apple-ios.cmake +++ b/toolchains/aarch64-apple-ios.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/arm64/make_arm64_aapcs_macho_gas.S - src/asm/arm64/jump_arm64_aapcs_macho_gas.S + src/asm/arm64/make_context_arm64_aapcs_macho_gas.S + src/asm/arm64/switch_arm64_aapcs_macho_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) diff --git a/toolchains/aarch64-pc-linux-gnu.cmake b/toolchains/aarch64-pc-linux-gnu.cmake index 406c260..67a7896 100644 --- a/toolchains/aarch64-pc-linux-gnu.cmake +++ b/toolchains/aarch64-pc-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/arm64/make_arm64_aapcs_elf_gas.S - src/asm/arm64/jump_arm64_aapcs_elf_gas.S + src/asm/arm64/make_context_arm64_aapcs_elf_gas.S + src/asm/arm64/switch_arm64_aapcs_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/aarch64-pc-windows-gnu.cmake b/toolchains/aarch64-pc-windows-gnu.cmake index 46d6d71..8e8ad06 100644 --- a/toolchains/aarch64-pc-windows-gnu.cmake +++ b/toolchains/aarch64-pc-windows-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/arm64/make_arm64_aapcs_pe_armclang.S - src/asm/arm64/jump_arm64_aapcs_pe_armclang.S + src/asm/arm64/make_context_arm64_aapcs_pe_armclang.S + src/asm/arm64/switch_arm64_aapcs_pe_armclang.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/aarch64-pc-windows-msvc.cmake b/toolchains/aarch64-pc-windows-msvc.cmake index b9b30f8..cec9528 100644 --- a/toolchains/aarch64-pc-windows-msvc.cmake +++ b/toolchains/aarch64-pc-windows-msvc.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/arm64/make_arm64_aapcs_pe_armasm.asm - src/asm/arm64/jump_arm64_aapcs_pe_armasm.asm + src/asm/arm64/make_context_arm64_aapcs_pe_armasm.asm + src/asm/arm64/switch_arm64_aapcs_pe_armasm.asm ) find_program(ARMASM64 diff --git a/toolchains/arm-unknown-linux-gnueabihf.cmake b/toolchains/arm-unknown-linux-gnueabihf.cmake index 54346b4..d6f0c5b 100644 --- a/toolchains/arm-unknown-linux-gnueabihf.cmake +++ b/toolchains/arm-unknown-linux-gnueabihf.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/arm/make_arm_aapcs_elf_gas.S - src/asm/arm/jump_arm_aapcs_elf_gas.S + src/asm/arm/make_context_arm_aapcs_elf_gas.S + src/asm/arm/switch_arm_aapcs_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/arm-unknown-linux-musleabihf.cmake b/toolchains/arm-unknown-linux-musleabihf.cmake index 5815768..6cad185 100644 --- a/toolchains/arm-unknown-linux-musleabihf.cmake +++ b/toolchains/arm-unknown-linux-musleabihf.cmake @@ -6,8 +6,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/arm/make_arm_aapcs_elf_gas.S - src/asm/arm/jump_arm_aapcs_elf_gas.S + src/asm/arm/make_context_arm_aapcs_elf_gas.S + src/asm/arm/switch_arm_aapcs_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2 -static) diff --git a/toolchains/i386-unknown-linux-gnu.cmake b/toolchains/i386-unknown-linux-gnu.cmake index a16ff7c..0471df0 100644 --- a/toolchains/i386-unknown-linux-gnu.cmake +++ b/toolchains/i386-unknown-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/i386/make_i386_sysv_elf_gas.S - src/asm/i386/jump_i386_sysv_elf_gas.S + src/asm/i386/make_context_i386_sysv_elf_gas.S + src/asm/i386/switch_i386_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2 -m32) \ No newline at end of file diff --git a/toolchains/mips64el-unknown-linux-gnuabi64.cmake b/toolchains/mips64el-unknown-linux-gnuabi64.cmake index 8c3b425..26af8af 100644 --- a/toolchains/mips64el-unknown-linux-gnuabi64.cmake +++ b/toolchains/mips64el-unknown-linux-gnuabi64.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/mips64/make_mips64_n64_elf_gas.S - src/asm/mips64/jump_mips64_n64_elf_gas.S + src/asm/mips64/make_context_mips64_n64_elf_gas.S + src/asm/mips64/switch_mips64_n64_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/mipsel-unknown-linux-gnu.cmake b/toolchains/mipsel-unknown-linux-gnu.cmake index c8f5b47..9d212ce 100644 --- a/toolchains/mipsel-unknown-linux-gnu.cmake +++ b/toolchains/mipsel-unknown-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/mips/make_mips32_o32_elf_gas.S - src/asm/mips/jump_mips32_o32_elf_gas.S + src/asm/mips/make_context_mips32_o32_elf_gas.S + src/asm/mips/switch_mips32_o32_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/powerpc-unknown-linux-gnu.cmake b/toolchains/powerpc-unknown-linux-gnu.cmake index 86c609d..6ab6632 100644 --- a/toolchains/powerpc-unknown-linux-gnu.cmake +++ b/toolchains/powerpc-unknown-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/ppc32/make_ppc32_sysv_elf_gas.S - src/asm/ppc32/jump_ppc32_sysv_elf_gas.S + src/asm/ppc32/make_context_ppc32_sysv_elf_gas.S + src/asm/ppc32/switch_ppc32_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/powerpc64le-unknown-linux-gnu.cmake b/toolchains/powerpc64le-unknown-linux-gnu.cmake index 44f4325..855b1b4 100644 --- a/toolchains/powerpc64le-unknown-linux-gnu.cmake +++ b/toolchains/powerpc64le-unknown-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/ppc64/make_ppc64_sysv_elf_gas.S - src/asm/ppc64/jump_ppc64_sysv_elf_gas.S + src/asm/ppc64/make_context_ppc64_sysv_elf_gas.S + src/asm/ppc64/switch_ppc64_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/powerpc64le-unknown-linux-musl.cmake b/toolchains/powerpc64le-unknown-linux-musl.cmake index 846f96d..2eff142 100644 --- a/toolchains/powerpc64le-unknown-linux-musl.cmake +++ b/toolchains/powerpc64le-unknown-linux-musl.cmake @@ -6,8 +6,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/ppc64/make_ppc64_sysv_elf_gas.S - src/asm/ppc64/jump_ppc64_sysv_elf_gas.S + src/asm/ppc64/make_context_ppc64_sysv_elf_gas.S + src/asm/ppc64/switch_ppc64_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2 -static) diff --git a/toolchains/riscv64-unknown-linux-gnu.cmake b/toolchains/riscv64-unknown-linux-gnu.cmake index c15f972..1d2da4d 100644 --- a/toolchains/riscv64-unknown-linux-gnu.cmake +++ b/toolchains/riscv64-unknown-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/riscv64/make_riscv64_sysv_elf_gas.S - src/asm/riscv64/jump_riscv64_sysv_elf_gas.S + src/asm/riscv64/make_context_riscv64_sysv_elf_gas.S + src/asm/riscv64/switch_riscv64_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/riscv64-unknown-linux-musl.cmake b/toolchains/riscv64-unknown-linux-musl.cmake index 0f368f5..e7c5bad 100644 --- a/toolchains/riscv64-unknown-linux-musl.cmake +++ b/toolchains/riscv64-unknown-linux-musl.cmake @@ -6,8 +6,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/riscv64/make_riscv64_sysv_elf_gas.S - src/asm/riscv64/jump_riscv64_sysv_elf_gas.S + src/asm/riscv64/make_context_riscv64_sysv_elf_gas.S + src/asm/riscv64/switch_riscv64_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2 -static) diff --git a/toolchains/s390x-ibm-linux-gnu.cmake b/toolchains/s390x-ibm-linux-gnu.cmake index a232033..19f748b 100644 --- a/toolchains/s390x-ibm-linux-gnu.cmake +++ b/toolchains/s390x-ibm-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/s390x/make_s390x_sysv_elf_gas.S - src/asm/s390x/jump_s390x_sysv_elf_gas.S + src/asm/s390x/make_context_s390x_sysv_elf_gas.S + src/asm/s390x/switch_s390x_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/s390x-ibm-linux-musl.cmake b/toolchains/s390x-ibm-linux-musl.cmake index e15d4cc..2d3a8f4 100644 --- a/toolchains/s390x-ibm-linux-musl.cmake +++ b/toolchains/s390x-ibm-linux-musl.cmake @@ -6,8 +6,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/s390x/make_s390x_sysv_elf_gas.S - src/asm/s390x/jump_s390x_sysv_elf_gas.S + src/asm/s390x/make_context_s390x_sysv_elf_gas.S + src/asm/s390x/switch_s390x_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2 -static) diff --git a/toolchains/sparc64-unknown-linux-gnu.cmake b/toolchains/sparc64-unknown-linux-gnu.cmake index e7f0478..e03f47c 100644 --- a/toolchains/sparc64-unknown-linux-gnu.cmake +++ b/toolchains/sparc64-unknown-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/sparc64/make_sparc64_sysv_elf_gas.S - src/asm/sparc64/jump_sparc64_sysv_elf_gas.S + src/asm/sparc64/make_context_sparc64_sysv_elf_gas.S + src/asm/sparc64/switch_sparc64_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/x86_64-apple-darwin.cmake b/toolchains/x86_64-apple-darwin.cmake index 1efa89e..f68681f 100644 --- a/toolchains/x86_64-apple-darwin.cmake +++ b/toolchains/x86_64-apple-darwin.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/x86_64/make_x86_64_sysv_macho_gas.S - src/asm/x86_64/jump_x86_64_sysv_macho_gas.S + src/asm/x86_64/make_context_x86_64_sysv_macho_gas.S + src/asm/x86_64/switch_x86_64_sysv_macho_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/x86_64-pc-linux-gnu.cmake b/toolchains/x86_64-pc-linux-gnu.cmake index 610bc6f..00bc0ff 100644 --- a/toolchains/x86_64-pc-linux-gnu.cmake +++ b/toolchains/x86_64-pc-linux-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/x86_64/make_x86_64_sysv_elf_gas.S - src/asm/x86_64/jump_x86_64_sysv_elf_gas.S + src/asm/x86_64/make_context_x86_64_sysv_elf_gas.S + src/asm/x86_64/switch_x86_64_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/x86_64-pc-windows-gnu.cmake b/toolchains/x86_64-pc-windows-gnu.cmake index eaa86b9..f2b8e24 100644 --- a/toolchains/x86_64-pc-windows-gnu.cmake +++ b/toolchains/x86_64-pc-windows-gnu.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/x86_64/make_x86_64_ms_pe_gas.S - src/asm/x86_64/jump_x86_64_ms_pe_gas.S + src/asm/x86_64/make_context_x86_64_ms_pe_gas.S + src/asm/x86_64/switch_x86_64_ms_pe_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file diff --git a/toolchains/x86_64-pc-windows-msvc.cmake b/toolchains/x86_64-pc-windows-msvc.cmake index 364b8b1..7af7a1d 100644 --- a/toolchains/x86_64-pc-windows-msvc.cmake +++ b/toolchains/x86_64-pc-windows-msvc.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/x86_64/make_x86_64_ms_pe_masm.asm - src/asm/x86_64/jump_x86_64_ms_pe_masm.asm + src/asm/x86_64/make_context_x86_64_ms_pe_masm.asm + src/asm/x86_64/switch_x86_64_ms_pe_masm.asm ) set(CMAKE_ASM_MASM_COMPILER ml64.exe CACHE FILEPATH "x64 MASM assembler" FORCE) diff --git a/toolchains/x86_64-unknown-linux-android.cmake b/toolchains/x86_64-unknown-linux-android.cmake index d661864..1da83bc 100644 --- a/toolchains/x86_64-unknown-linux-android.cmake +++ b/toolchains/x86_64-unknown-linux-android.cmake @@ -4,8 +4,8 @@ # See LICENSE file for details set(ASM_FILES - src/asm/x86_64/make_x86_64_sysv_elf_gas.S - src/asm/x86_64/jump_x86_64_sysv_elf_gas.S + src/asm/x86_64/make_context_x86_64_sysv_elf_gas.S + src/asm/x86_64/switch_x86_64_sysv_elf_gas.S ) enable_language(ASM) add_compile_options(-Wall -Wextra -Werror -g -O2) \ No newline at end of file From 5c16aeff6c8fe78853ee17bb25740d5e6f04e26f Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Tue, 28 Apr 2026 13:51:34 -0700 Subject: [PATCH 06/14] Add test_yafl_guard back into tests on macOS. The signal raised is consistent, but weird. Sig 4 is SIGILL. Co-authored-by: Copilot --- CMakeLists.txt | 6 - docs/coverage.svg | 4 +- docs/coverage/index-sort-f.html | 28 +- docs/coverage/index-sort-l.html | 28 +- docs/coverage/index.html | 28 +- docs/coverage/src/index.html | 2 +- docs/coverage/src/yafl.c.func-c.html | 24 +- docs/coverage/src/yafl.c.func.html | 24 +- docs/coverage/src/yafl.c.gcov.html | 250 +++++++------- docs/coverage/tests/index-sort-f.html | 26 +- docs/coverage/tests/index-sort-l.html | 26 +- docs/coverage/tests/index.html | 26 +- .../tests/test_yafl_basic.c.func-c.html | 28 +- .../tests/test_yafl_basic.c.func.html | 28 +- .../tests/test_yafl_basic.c.gcov.html | 314 +++++++++--------- .../tests/test_yafl_guard.c.func-c.html | 21 +- .../tests/test_yafl_guard.c.func.html | 21 +- .../tests/test_yafl_guard.c.gcov.html | 88 ++--- .../tests/test_yafl_many.c.func-c.html | 6 +- .../coverage/tests/test_yafl_many.c.func.html | 6 +- .../coverage/tests/test_yafl_many.c.gcov.html | 158 ++++----- .../test_yafl_suspend_resume.c.func-c.html | 24 +- .../test_yafl_suspend_resume.c.func.html | 24 +- .../test_yafl_suspend_resume.c.gcov.html | 224 ++++++------- 24 files changed, 727 insertions(+), 687 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6214449..293bb92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,12 +240,6 @@ include(CTest) set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED TRUE) foreach(test ${TESTS}) - # Skip guard page test on macOS when coverage is enabled (coverage instrumentation changes signal behavior) - if(ENABLE_COVERAGE AND APPLE AND "${test}" STREQUAL "test_yafl_guard") - message(STATUS "Skipping ${test} on macOS (coverage enabled)") - continue() - endif() - # Use absolute path to binary so tests run from build directory add_test(NAME ${test} COMMAND ${test}) endforeach() diff --git a/docs/coverage.svg b/docs/coverage.svg index 0d7ba5a..235b19e 100644 --- a/docs/coverage.svg +++ b/docs/coverage.svg @@ -8,13 +8,13 @@ - + coverage - 97.3% + 89.3% diff --git a/docs/coverage/index-sort-f.html b/docs/coverage/index-sort-f.html index 726501a..2eb7a84 100644 --- a/docs/coverage/index-sort-f.html +++ b/docs/coverage/index-sort-f.html @@ -31,18 +31,18 @@ yafl Code Coverage Lines: - 97.3 % - 560 - 545 + 89.3 % + 642 + 573 Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: - 95.7 % - 47 - 45 + 90.2 % + 51 + 46 @@ -94,14 +94,14 @@ tests/ -
100.0%
+
89.3%89.3%
- 100.0 % - 421 - 421 - 100.0 % - 34 - 34 + 89.3 % + 503 + 449 + 92.1 % + 38 + 35 diff --git a/docs/coverage/index-sort-l.html b/docs/coverage/index-sort-l.html index a9dada5..e410472 100644 --- a/docs/coverage/index-sort-l.html +++ b/docs/coverage/index-sort-l.html @@ -31,18 +31,18 @@ yafl Code Coverage Lines: - 97.3 % - 560 - 545 + 89.3 % + 642 + 573 Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: - 95.7 % - 47 - 45 + 90.2 % + 51 + 46 @@ -94,14 +94,14 @@ tests/ -
100.0%
+
89.3%89.3%
- 100.0 % - 421 - 421 - 100.0 % - 34 - 34 + 89.3 % + 503 + 449 + 92.1 % + 38 + 35 diff --git a/docs/coverage/index.html b/docs/coverage/index.html index 406f0e7..577c07f 100644 --- a/docs/coverage/index.html +++ b/docs/coverage/index.html @@ -31,18 +31,18 @@ yafl Code Coverage Lines: - 97.3 % - 560 - 545 + 89.3 % + 642 + 573 Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: - 95.7 % - 47 - 45 + 90.2 % + 51 + 46 @@ -94,14 +94,14 @@ tests/ -
100.0%
+
89.3%89.3%
- 100.0 % - 421 - 421 - 100.0 % - 34 - 34 + 89.3 % + 503 + 449 + 92.1 % + 38 + 35 diff --git a/docs/coverage/src/index.html b/docs/coverage/src/index.html index 75aae56..999849e 100644 --- a/docs/coverage/src/index.html +++ b/docs/coverage/src/index.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 84.6 % diff --git a/docs/coverage/src/yafl.c.func-c.html b/docs/coverage/src/yafl.c.func-c.html index 8705079..42a3410 100644 --- a/docs/coverage/src/yafl.c.func-c.html +++ b/docs/coverage/src/yafl.c.func-c.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 84.6 % @@ -82,7 +82,7 @@ reinitialize_fiber_context_with_watermark - 16 + 24 @@ -90,7 +90,7 @@ yafl_fiber_stack_high_watermark - 30 + 45 @@ -98,7 +98,7 @@ get_page_size - 214 + 321 @@ -106,7 +106,7 @@ align_stack_pointer - 244 + 366 @@ -114,7 +114,7 @@ free_fiber_stack - 244 + 366 @@ -122,7 +122,7 @@ yafl_fiber_destroy - 248 + 372 @@ -130,7 +130,7 @@ yafl_fiber_create - 252 + 378 @@ -138,7 +138,7 @@ initialize_fiber_context - 260 + 390 @@ -146,7 +146,7 @@ yafl_fiber_suspend - 2050 + 3075 @@ -154,7 +154,7 @@ yafl_fiber_resume - 2290 + 3435 @@ -162,7 +162,7 @@ yafl_fiber_status - 2864 + 4296 diff --git a/docs/coverage/src/yafl.c.func.html b/docs/coverage/src/yafl.c.func.html index 3a7a6ba..c970366 100644 --- a/docs/coverage/src/yafl.c.func.html +++ b/docs/coverage/src/yafl.c.func.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 84.6 % @@ -66,7 +66,7 @@ align_stack_pointer - 244 + 366 @@ -82,7 +82,7 @@ free_fiber_stack - 244 + 366 @@ -90,7 +90,7 @@ get_page_size - 214 + 321 @@ -98,7 +98,7 @@ initialize_fiber_context - 260 + 390 @@ -106,7 +106,7 @@ reinitialize_fiber_context_with_watermark - 16 + 24 @@ -114,7 +114,7 @@ yafl_fiber_create - 252 + 378 @@ -122,7 +122,7 @@ yafl_fiber_destroy - 248 + 372 @@ -130,7 +130,7 @@ yafl_fiber_resume - 2290 + 3435 @@ -138,7 +138,7 @@ yafl_fiber_stack_high_watermark - 30 + 45 @@ -146,7 +146,7 @@ yafl_fiber_status - 2864 + 4296 @@ -154,7 +154,7 @@ yafl_fiber_suspend - 2050 + 3075 diff --git a/docs/coverage/src/yafl.c.gcov.html b/docs/coverage/src/yafl.c.gcov.html index f86cdc2..f4e72ab 100644 --- a/docs/coverage/src/yafl.c.gcov.html +++ b/docs/coverage/src/yafl.c.gcov.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 84.6 % @@ -143,55 +143,55 @@ 81 : 82 : static void fiber_entry_trampoline(void *arg); 83 : - 84 244 : static void free_fiber_stack(yafl_fiber_t *fiber) { - 85 244 : if(fiber == NULL || fiber->stack_region == NULL) { return; } + 84 366 : static void free_fiber_stack(yafl_fiber_t *fiber) { + 85 366 : if(fiber == NULL || fiber->stack_region == NULL) { return; } 86 : - 87 244 : if(fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { - 88 30 : free(fiber->stack_region); - 89 244 : } else if(fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { + 87 366 : if(fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { + 88 45 : free(fiber->stack_region); + 89 366 : } else if(fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { 90 : #ifdef _WIN32 91 : VirtualFree(fiber->stack_region, 0, MEM_RELEASE); 92 : #else - 93 214 : munmap(fiber->stack_region, fiber->stack_total_size); + 93 321 : munmap(fiber->stack_region, fiber->stack_total_size); 94 : #endif - 95 214 : } + 95 321 : } 96 : - 97 244 : fiber->stack_region = NULL; - 98 244 : fiber->stack_total_size = 0; - 99 244 : fiber->stack_top = NULL; - 100 244 : fiber->stack_size = 0; - 101 244 : } + 97 366 : fiber->stack_region = NULL; + 98 366 : fiber->stack_total_size = 0; + 99 366 : fiber->stack_top = NULL; + 100 366 : fiber->stack_size = 0; + 101 366 : } 102 : - 103 260 : static bool initialize_fiber_context(yafl_fiber_t *fiber) { - 104 260 : fiber->context = yafl_make_context(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); - 105 260 : return fiber->context != NULL; + 103 390 : static bool initialize_fiber_context(yafl_fiber_t *fiber) { + 104 390 : fiber->context = yafl_make_context(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); + 105 390 : return fiber->context != NULL; 106 : } 107 : - 108 16 : static bool reinitialize_fiber_context_with_watermark(yafl_fiber_t *fiber) { - 109 16 : memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); - 110 16 : return initialize_fiber_context(fiber); + 108 24 : static bool reinitialize_fiber_context_with_watermark(yafl_fiber_t *fiber) { + 109 24 : memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); + 110 24 : return initialize_fiber_context(fiber); 111 : } 112 : 113 : /* ======================================================================== 114 : * Utility Functions 115 : * ======================================================================== */ 116 : - 117 214 : static size_t get_page_size(void) { + 117 321 : static size_t get_page_size(void) { 118 : #ifdef _WIN32 119 : SYSTEM_INFO si; 120 : GetSystemInfo(&si); 121 : return (size_t)si.dwPageSize; 122 : #else - 123 214 : long page_size = sysconf(_SC_PAGE_SIZE); - 124 214 : if(page_size <= 0) { return 4096; } - 125 214 : return (size_t)page_size; + 123 321 : long page_size = sysconf(_SC_PAGE_SIZE); + 124 321 : if(page_size <= 0) { return 4096; } + 125 321 : return (size_t)page_size; 126 : #endif - 127 214 : } + 127 321 : } 128 : - 129 244 : static void *align_stack_pointer(void *ptr) { - 130 244 : uintptr_t addr = (uintptr_t)ptr; - 131 488 : return (void *)(addr & ~((uintptr_t)FCONTEXT_STACK_ALIGNMENT - 1)); - 132 244 : } + 129 366 : static void *align_stack_pointer(void *ptr) { + 130 366 : uintptr_t addr = (uintptr_t)ptr; + 131 732 : return (void *)(addr & ~((uintptr_t)FCONTEXT_STACK_ALIGNMENT - 1)); + 132 366 : } 133 : 134 : /* ======================================================================== 135 : * Trampoline: Adapts Low-Level API to High-Level Fiber API @@ -228,47 +228,47 @@ 166 : * Fiber Creation 167 : * ======================================================================== */ 168 : - 169 252 : extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, yafl_stack_flags_t flags) { + 169 378 : extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, yafl_stack_flags_t flags) { 170 : /* Validate fiber function is not NULL */ - 171 252 : if(fiber_fn == NULL) { return NULL; } + 171 378 : if(fiber_fn == NULL) { return NULL; } 172 : 173 : /* Validate exactly one allocation type is set */ - 174 250 : int alloc_flags = flags & (YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_VMEM); - 175 250 : if(alloc_flags != YAFL_STACK_FLAGS_MALLOC && alloc_flags != YAFL_STACK_FLAGS_VMEM) { return NULL; } + 174 375 : int alloc_flags = flags & (YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_VMEM); + 175 375 : if(alloc_flags != YAFL_STACK_FLAGS_MALLOC && alloc_flags != YAFL_STACK_FLAGS_VMEM) { return NULL; } 176 : - 177 244 : bool use_vmem = (flags & YAFL_STACK_FLAGS_VMEM) != 0; - 178 244 : bool use_watermark = (flags & YAFL_STACK_FLAGS_WATERMARK) != 0; + 177 366 : bool use_vmem = (flags & YAFL_STACK_FLAGS_VMEM) != 0; + 178 366 : bool use_watermark = (flags & YAFL_STACK_FLAGS_WATERMARK) != 0; 179 : 180 : /* Allocate fiber structure */ - 181 244 : yafl_fiber_t *fiber = malloc(sizeof(yafl_fiber_t)); - 182 244 : if(fiber == NULL) { return NULL; } + 181 366 : yafl_fiber_t *fiber = malloc(sizeof(yafl_fiber_t)); + 182 366 : if(fiber == NULL) { return NULL; } 183 : 184 : /* Initialize fiber structure */ - 185 244 : fiber->magic = FCONTEXT_FIBER_MAGIC; - 186 244 : fiber->alloc_type = use_vmem ? FCONTEXT_ALLOC_VMEM : FCONTEXT_ALLOC_MALLOC; - 187 244 : fiber->status = YAFL_FIBER_STATUS_SUSPENDED; - 188 244 : fiber->user_entry = fiber_fn; - 189 244 : fiber->cached_result = NULL; - 190 244 : fiber->watermark_filled = use_watermark; - 191 244 : fiber->context = NULL; - 192 244 : fiber->resumer_context = NULL; - 193 244 : fiber->stack_region = NULL; - 194 244 : fiber->stack_total_size = 0; - 195 244 : fiber->stack_top = NULL; - 196 244 : fiber->stack_size = 0; + 185 366 : fiber->magic = FCONTEXT_FIBER_MAGIC; + 186 366 : fiber->alloc_type = use_vmem ? FCONTEXT_ALLOC_VMEM : FCONTEXT_ALLOC_MALLOC; + 187 366 : fiber->status = YAFL_FIBER_STATUS_SUSPENDED; + 188 366 : fiber->user_entry = fiber_fn; + 189 366 : fiber->cached_result = NULL; + 190 366 : fiber->watermark_filled = use_watermark; + 191 366 : fiber->context = NULL; + 192 366 : fiber->resumer_context = NULL; + 193 366 : fiber->stack_region = NULL; + 194 366 : fiber->stack_total_size = 0; + 195 366 : fiber->stack_top = NULL; + 196 366 : fiber->stack_size = 0; 197 : 198 : /* Use default stack size if not specified */ - 199 244 : if(stack_size == 0) { stack_size = FCONTEXT_DEFAULT_STACK_SIZE; } + 199 366 : if(stack_size == 0) { stack_size = FCONTEXT_DEFAULT_STACK_SIZE; } 200 : 201 : /* Allocate stack based on allocation type */ - 202 244 : if(use_vmem) { - 203 214 : size_t page_size = get_page_size(); - 204 214 : size_t stack_with_overhead = stack_size + 256; - 205 214 : size_t aligned_stack_size = ((stack_with_overhead + page_size - 1) / page_size) * page_size; - 206 214 : size_t guard_size = page_size; - 207 214 : size_t total_size = guard_size + aligned_stack_size + guard_size; + 202 366 : if(use_vmem) { + 203 321 : size_t page_size = get_page_size(); + 204 321 : size_t stack_with_overhead = stack_size + 256; + 205 321 : size_t aligned_stack_size = ((stack_with_overhead + page_size - 1) / page_size) * page_size; + 206 321 : size_t guard_size = page_size; + 207 321 : size_t total_size = guard_size + aligned_stack_size + guard_size; 208 : - 209 214 : void *region = NULL; + 209 321 : void *region = NULL; 210 : #ifdef _WIN32 211 : region = VirtualAlloc(NULL, total_size, MEM_RESERVE, PAGE_NOACCESS); 212 : if(region == NULL) { goto create_fail; } @@ -277,140 +277,140 @@ 215 : void *stack_base = (char *)region + guard_size; 216 : if(!VirtualAlloc(stack_base, aligned_stack_size, MEM_COMMIT, PAGE_READWRITE)) { goto create_fail; } 217 : #else - 218 214 : region = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - 219 214 : if(region == MAP_FAILED) { goto create_fail; } - 220 214 : fiber->stack_region = region; - 221 214 : fiber->stack_total_size = total_size; - 222 214 : if(mprotect(region, guard_size, PROT_NONE) == -1) { goto create_fail; } - 223 214 : if(mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { goto create_fail; } - 224 214 : void *stack_base = (char *)region + guard_size; + 218 321 : region = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + 219 321 : if(region == MAP_FAILED) { goto create_fail; } + 220 321 : fiber->stack_region = region; + 221 321 : fiber->stack_total_size = total_size; + 222 321 : if(mprotect(region, guard_size, PROT_NONE) == -1) { goto create_fail; } + 223 321 : if(mprotect((char *)region + guard_size + aligned_stack_size, guard_size, PROT_NONE) == -1) { goto create_fail; } + 224 321 : void *stack_base = (char *)region + guard_size; 225 : #endif 226 : - 227 214 : void *stack_region_end = (char *)stack_base + aligned_stack_size; - 228 214 : void *stack_top = (char *)stack_region_end - 256; - 229 214 : stack_top = align_stack_pointer(stack_top); - 230 214 : size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)stack_base); + 227 321 : void *stack_region_end = (char *)stack_base + aligned_stack_size; + 228 321 : void *stack_top = (char *)stack_region_end - 256; + 229 321 : stack_top = align_stack_pointer(stack_top); + 230 321 : size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)stack_base); 231 : - 232 214 : fiber->stack_top = stack_top; - 233 214 : fiber->stack_size = actual_stack_size; - 234 214 : } else { + 232 321 : fiber->stack_top = stack_top; + 233 321 : fiber->stack_size = actual_stack_size; + 234 321 : } else { 235 : /* malloc allocation */ - 236 30 : size_t allocated_size = stack_size + 256; - 237 30 : void *block = malloc(allocated_size); - 238 30 : if(block == NULL) { goto create_fail; } - 239 30 : fiber->stack_region = block; - 240 30 : fiber->stack_total_size = allocated_size; + 236 45 : size_t allocated_size = stack_size + 256; + 237 45 : void *block = malloc(allocated_size); + 238 45 : if(block == NULL) { goto create_fail; } + 239 45 : fiber->stack_region = block; + 240 45 : fiber->stack_total_size = allocated_size; 241 : - 242 30 : void *block_end = (char *)block + allocated_size; - 243 30 : void *stack_top = (char *)block_end - 256; - 244 30 : stack_top = align_stack_pointer(stack_top); + 242 45 : void *block_end = (char *)block + allocated_size; + 243 45 : void *stack_top = (char *)block_end - 256; + 244 45 : stack_top = align_stack_pointer(stack_top); 245 : - 246 30 : size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)block); + 246 45 : size_t actual_stack_size = (uintptr_t)(intptr_t)((char *)stack_top - (char *)block); 247 : - 248 30 : fiber->stack_top = stack_top; - 249 30 : fiber->stack_size = actual_stack_size; - 250 30 : } + 248 45 : fiber->stack_top = stack_top; + 249 45 : fiber->stack_size = actual_stack_size; + 250 45 : } 251 : 252 : /* Initialize the low-level context with trampoline as entry */ - 253 244 : if(!initialize_fiber_context(fiber)) { goto create_fail; } + 253 366 : if(!initialize_fiber_context(fiber)) { goto create_fail; } 254 : 255 : /* Apply watermark if requested */ - 256 244 : if(use_watermark) { - 257 16 : if(!reinitialize_fiber_context_with_watermark(fiber)) { goto create_fail; } - 258 16 : } + 256 366 : if(use_watermark) { + 257 24 : if(!reinitialize_fiber_context_with_watermark(fiber)) { goto create_fail; } + 258 24 : } 259 : - 260 244 : return fiber; + 260 366 : return fiber; 261 : 262 : create_fail: 263 0 : free_fiber_stack(fiber); 264 0 : free(fiber); 265 0 : return NULL; - 266 252 : } + 266 378 : } 267 : 268 : /* ======================================================================== 269 : * Fiber Control Flow 270 : * ======================================================================== */ 271 : - 272 2290 : extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { + 272 3435 : extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { 273 : /* Validation */ - 274 2290 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return NULL; } + 274 3435 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return NULL; } 275 : 276 : /* If complete, return cached result (idempotent) */ - 277 2288 : if(fiber->status == YAFL_FIBER_STATUS_COMPLETE) { return fiber->cached_result; } + 277 3432 : if(fiber->status == YAFL_FIBER_STATUS_COMPLETE) { return fiber->cached_result; } 278 : 279 : /* Cannot resume running fiber */ - 280 2286 : if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return NULL; } + 280 3429 : if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return NULL; } 281 : 282 : /* Update status and TLS */ - 283 2286 : fiber->status = YAFL_FIBER_STATUS_RUNNING; - 284 2286 : tls_current_fiber = fiber; + 283 3429 : fiber->status = YAFL_FIBER_STATUS_RUNNING; + 284 3429 : tls_current_fiber = fiber; 285 : 286 : /* Perform context switch */ - 287 2286 : void *result = yafl_switch(&fiber->resumer_context, fiber->context, arg); + 287 3429 : void *result = yafl_switch(&fiber->resumer_context, fiber->context, arg); 288 : 289 : /* Back in resumer */ - 290 2286 : tls_current_fiber = NULL; + 290 3429 : tls_current_fiber = NULL; 291 : - 292 2286 : return result; - 293 2290 : } + 292 3429 : return result; + 293 3435 : } 294 : - 295 2050 : extern void *yafl_fiber_suspend(void *result) { - 296 2050 : yafl_fiber_t *current = tls_current_fiber; + 295 3075 : extern void *yafl_fiber_suspend(void *result) { + 296 3075 : yafl_fiber_t *current = tls_current_fiber; 297 : 298 : /* Must be in a fiber */ - 299 2050 : if(current == NULL) { return NULL; } + 299 3075 : if(current == NULL) { return NULL; } 300 : 301 : /* Update status */ - 302 2050 : current->status = YAFL_FIBER_STATUS_SUSPENDED; + 302 3075 : current->status = YAFL_FIBER_STATUS_SUSPENDED; 303 : 304 : /* Switch back to resumer */ - 305 2050 : void *arg = yafl_switch(&current->context, current->resumer_context, result); + 305 3075 : void *arg = yafl_switch(&current->context, current->resumer_context, result); 306 : 307 : /* When resumed - restore state */ - 308 2050 : current->status = YAFL_FIBER_STATUS_RUNNING; - 309 2050 : tls_current_fiber = current; + 308 3075 : current->status = YAFL_FIBER_STATUS_RUNNING; + 309 3075 : tls_current_fiber = current; 310 : 311 : /* Return argument passed to resume */ - 312 2050 : return arg; - 313 2050 : } + 312 3075 : return arg; + 313 3075 : } 314 : 315 : /* ======================================================================== 316 : * Fiber Status and Monitoring 317 : * ======================================================================== */ 318 : - 319 2864 : extern yafl_fiber_status_t yafl_fiber_status(yafl_fiber_t *fiber) { - 320 2864 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return YAFL_FIBER_STATUS_ERR; } - 321 2862 : return fiber->status; - 322 2864 : } + 319 4296 : extern yafl_fiber_status_t yafl_fiber_status(yafl_fiber_t *fiber) { + 320 4296 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return YAFL_FIBER_STATUS_ERR; } + 321 4293 : return fiber->status; + 322 4296 : } 323 : - 324 30 : extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { - 325 30 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC || !fiber->watermark_filled) { return 0; } + 324 45 : extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { + 325 45 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC || !fiber->watermark_filled) { return 0; } 326 : 327 : /* Scan from stack base for watermark bytes */ - 328 16 : unsigned char *stack_base = (unsigned char *)fiber->stack_top - fiber->stack_size; - 329 16 : size_t unused = 0; + 328 24 : unsigned char *stack_base = (unsigned char *)fiber->stack_top - fiber->stack_size; + 329 24 : size_t unused = 0; 330 : - 331 386960 : while(unused < fiber->stack_size && stack_base[unused] == FCONTEXT_STACK_WATERMARK) { unused++; } + 331 580440 : while(unused < fiber->stack_size && stack_base[unused] == FCONTEXT_STACK_WATERMARK) { unused++; } 332 : - 333 16 : return fiber->stack_size - unused; - 334 30 : } + 333 24 : return fiber->stack_size - unused; + 334 45 : } 335 : 336 : /* ======================================================================== 337 : * Cleanup 338 : * ======================================================================== */ 339 : - 340 248 : extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { - 341 248 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return; } + 340 372 : extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { + 341 372 : if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return; } 342 : 343 : /* Cannot destroy running fiber */ - 344 244 : if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return; } + 344 366 : if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return; } 345 : - 346 244 : free_fiber_stack(fiber); + 346 366 : free_fiber_stack(fiber); 347 : 348 : /* Invalidate and free */ - 349 244 : fiber->magic = 0; - 350 244 : free(fiber); - 351 248 : } + 349 366 : fiber->magic = 0; + 350 366 : free(fiber); + 351 372 : } 352 : 353 : /* ======================================================================== 354 : * Utilities diff --git a/docs/coverage/tests/index-sort-f.html b/docs/coverage/tests/index-sort-f.html index aac9999..152700d 100644 --- a/docs/coverage/tests/index-sort-f.html +++ b/docs/coverage/tests/index-sort-f.html @@ -31,18 +31,18 @@ yafl Code Coverage Lines: - 100.0 % - 421 - 421 + 89.3 % + 503 + 449 Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: - 100.0 % - 34 - 34 + 92.1 % + 38 + 35 @@ -79,6 +79,18 @@ Total Hit + + test_yafl_guard.c + +
34.1%34.1%
+ + 34.1 % + 82 + 28 + 25.0 % + 4 + 1 + test_yafl_many.c diff --git a/docs/coverage/tests/index-sort-l.html b/docs/coverage/tests/index-sort-l.html index 85c211f..15ca397 100644 --- a/docs/coverage/tests/index-sort-l.html +++ b/docs/coverage/tests/index-sort-l.html @@ -31,18 +31,18 @@ yafl Code Coverage Lines: - 100.0 % - 421 - 421 + 89.3 % + 503 + 449 Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: - 100.0 % - 34 - 34 + 92.1 % + 38 + 35 @@ -79,6 +79,18 @@ Total Hit + + test_yafl_guard.c + +
34.1%34.1%
+ + 34.1 % + 82 + 28 + 25.0 % + 4 + 1 + test_yafl_watermark.c diff --git a/docs/coverage/tests/index.html b/docs/coverage/tests/index.html index e704031..03b1152 100644 --- a/docs/coverage/tests/index.html +++ b/docs/coverage/tests/index.html @@ -31,18 +31,18 @@ yafl Code Coverage Lines: - 100.0 % - 421 - 421 + 89.3 % + 503 + 449 Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: - 100.0 % - 34 - 34 + 92.1 % + 38 + 35 @@ -91,6 +91,18 @@ 13 13 + + test_yafl_guard.c + +
34.1%34.1%
+ + 34.1 % + 82 + 28 + 25.0 % + 4 + 1 + test_yafl_many.c diff --git a/docs/coverage/tests/test_yafl_basic.c.func-c.html b/docs/coverage/tests/test_yafl_basic.c.func-c.html index c82f80f..78f35ae 100644 --- a/docs/coverage/tests/test_yafl_basic.c.func-c.html +++ b/docs/coverage/tests/test_yafl_basic.c.func-c.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -66,7 +66,7 @@ cycling_fiber_func - 2 + 3 @@ -74,7 +74,7 @@ main - 2 + 3 @@ -82,7 +82,7 @@ test_default_stack_size - 2 + 3 @@ -90,7 +90,7 @@ test_flag_combinations - 2 + 3 @@ -98,7 +98,7 @@ test_null_entry_function - 2 + 3 @@ -106,7 +106,7 @@ test_null_fiber_pointer - 2 + 3 @@ -114,7 +114,7 @@ test_simple_execution - 2 + 3 @@ -122,7 +122,7 @@ test_status_query - 2 + 3 @@ -130,7 +130,7 @@ test_suspend_resume_cycles - 2 + 3 @@ -138,7 +138,7 @@ test_watermark - 2 + 3 @@ -146,7 +146,7 @@ test_watermark_without_flag - 2 + 3 @@ -154,7 +154,7 @@ watermarked_fiber_func - 4 + 6 @@ -162,7 +162,7 @@ simple_fiber_func - 8 + 12 diff --git a/docs/coverage/tests/test_yafl_basic.c.func.html b/docs/coverage/tests/test_yafl_basic.c.func.html index 64e1b21..d0f5bdf 100644 --- a/docs/coverage/tests/test_yafl_basic.c.func.html +++ b/docs/coverage/tests/test_yafl_basic.c.func.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -66,7 +66,7 @@ cycling_fiber_func - 2 + 3 @@ -74,7 +74,7 @@ main - 2 + 3 @@ -82,7 +82,7 @@ simple_fiber_func - 8 + 12 @@ -90,7 +90,7 @@ test_default_stack_size - 2 + 3 @@ -98,7 +98,7 @@ test_flag_combinations - 2 + 3 @@ -106,7 +106,7 @@ test_null_entry_function - 2 + 3 @@ -114,7 +114,7 @@ test_null_fiber_pointer - 2 + 3 @@ -122,7 +122,7 @@ test_simple_execution - 2 + 3 @@ -130,7 +130,7 @@ test_status_query - 2 + 3 @@ -138,7 +138,7 @@ test_suspend_resume_cycles - 2 + 3 @@ -146,7 +146,7 @@ test_watermark - 2 + 3 @@ -154,7 +154,7 @@ test_watermark_without_flag - 2 + 3 @@ -162,7 +162,7 @@ watermarked_fiber_func - 4 + 6 diff --git a/docs/coverage/tests/test_yafl_basic.c.gcov.html b/docs/coverage/tests/test_yafl_basic.c.gcov.html index b102721..75ff469 100644 --- a/docs/coverage/tests/test_yafl_basic.c.gcov.html +++ b/docs/coverage/tests/test_yafl_basic.c.gcov.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -79,313 +79,313 @@ 17 : * Test: Simple Fiber Execution 18 : * ======================================================================== */ 19 : - 20 8 : static void *simple_fiber_func(void *arg) { - 21 8 : return arg; + 20 12 : static void *simple_fiber_func(void *arg) { + 21 12 : return arg; 22 : } 23 : - 24 2 : static void test_simple_execution(void) { - 25 2 : printf("test_simple_execution: "); - 26 2 : fflush(stdout); + 24 3 : static void test_simple_execution(void) { + 25 3 : printf("test_simple_execution: "); + 26 3 : fflush(stdout); 27 : 28 : /* Create fiber with malloc allocation */ - 29 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 29 3 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, 30 : YAFL_STACK_FLAGS_MALLOC); - 31 2 : assert(fiber != NULL); - 32 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 31 3 : assert(fiber != NULL); + 32 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 33 : 34 : /* Resume fiber */ - 35 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1234); - 36 2 : assert(result == (void *)0x1234); - 37 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 35 3 : void *result = yafl_fiber_resume(fiber, (void *)0x1234); + 36 3 : assert(result == (void *)0x1234); + 37 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 38 : 39 : /* Resuming completed fiber returns cached result */ - 40 2 : void *result2 = yafl_fiber_resume(fiber, (void *)0x5678); - 41 2 : assert(result2 == (void *)0x1234); + 40 3 : void *result2 = yafl_fiber_resume(fiber, (void *)0x5678); + 41 3 : assert(result2 == (void *)0x1234); 42 : 43 : /* Cleanup */ - 44 2 : yafl_fiber_destroy(fiber); - 45 2 : yafl_fiber_destroy(NULL); /* Safe to call on NULL */ + 44 3 : yafl_fiber_destroy(fiber); + 45 3 : yafl_fiber_destroy(NULL); /* Safe to call on NULL */ 46 : - 47 2 : printf("PASS\n"); - 48 2 : } + 47 3 : printf("PASS\n"); + 48 3 : } 49 : 50 : /* ======================================================================== 51 : * Test: Flag Combinations 52 : * ======================================================================== */ 53 : - 54 2 : static void test_flag_combinations(void) { - 55 2 : printf("test_flag_combinations: "); - 56 2 : fflush(stdout); + 54 3 : static void test_flag_combinations(void) { + 55 3 : printf("test_flag_combinations: "); + 56 3 : fflush(stdout); 57 : 58 : /* Valid: MALLOC alone */ - 59 2 : yafl_fiber_t *f1 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 59 3 : yafl_fiber_t *f1 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 60 : YAFL_STACK_FLAGS_MALLOC); - 61 2 : assert(f1 != NULL); - 62 2 : yafl_fiber_destroy(f1); + 61 3 : assert(f1 != NULL); + 62 3 : yafl_fiber_destroy(f1); 63 : 64 : /* Valid: VMEM alone */ - 65 2 : yafl_fiber_t *f2 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 65 3 : yafl_fiber_t *f2 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 66 : YAFL_STACK_FLAGS_VMEM); - 67 2 : assert(f2 != NULL); - 68 2 : yafl_fiber_destroy(f2); + 67 3 : assert(f2 != NULL); + 68 3 : yafl_fiber_destroy(f2); 69 : 70 : /* Valid: MALLOC with WATERMARK */ - 71 2 : yafl_fiber_t *f3 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 71 3 : yafl_fiber_t *f3 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 72 : YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_WATERMARK); - 73 2 : assert(f3 != NULL); - 74 2 : yafl_fiber_destroy(f3); + 73 3 : assert(f3 != NULL); + 74 3 : yafl_fiber_destroy(f3); 75 : 76 : /* Valid: VMEM with WATERMARK */ - 77 2 : yafl_fiber_t *f4 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 77 3 : yafl_fiber_t *f4 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 78 : YAFL_STACK_FLAGS_VMEM | YAFL_STACK_FLAGS_WATERMARK); - 79 2 : assert(f4 != NULL); - 80 2 : yafl_fiber_destroy(f4); + 79 3 : assert(f4 != NULL); + 80 3 : yafl_fiber_destroy(f4); 81 : 82 : /* Invalid: NONE */ - 83 2 : yafl_fiber_t *f5 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 83 3 : yafl_fiber_t *f5 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 84 : YAFL_STACK_FLAGS_NONE); - 85 2 : assert(f5 == NULL); + 85 3 : assert(f5 == NULL); 86 : 87 : /* Invalid: MALLOC | VMEM both set */ - 88 2 : yafl_fiber_t *f6 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 88 3 : yafl_fiber_t *f6 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 89 : YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_VMEM); - 90 2 : assert(f6 == NULL); + 90 3 : assert(f6 == NULL); 91 : 92 : /* Invalid: Neither allocation type */ - 93 2 : yafl_fiber_t *f7 = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 93 3 : yafl_fiber_t *f7 = yafl_fiber_create(simple_fiber_func, 16 * 1024, 94 : YAFL_STACK_FLAGS_WATERMARK); - 95 2 : assert(f7 == NULL); + 95 3 : assert(f7 == NULL); 96 : - 97 2 : printf("PASS\n"); - 98 2 : } + 97 3 : printf("PASS\n"); + 98 3 : } 99 : 100 : /* ======================================================================== 101 : * Test: Suspend/Resume Cycles 102 : * ======================================================================== */ 103 : - 104 2 : static void *cycling_fiber_func(void *arg) { - 105 2 : void *result = arg; + 104 3 : static void *cycling_fiber_func(void *arg) { + 105 3 : void *result = arg; 106 : - 107 12 : for (int i = 0; i < 5; i++) { - 108 10 : result = yafl_fiber_suspend(result); - 109 10 : } + 107 18 : for (int i = 0; i < 5; i++) { + 108 15 : result = yafl_fiber_suspend(result); + 109 15 : } 110 : - 111 4 : return result; - 112 2 : } + 111 6 : return result; + 112 3 : } 113 : - 114 2 : static void test_suspend_resume_cycles(void) { - 115 2 : printf("test_suspend_resume_cycles: "); - 116 2 : fflush(stdout); + 114 3 : static void test_suspend_resume_cycles(void) { + 115 3 : printf("test_suspend_resume_cycles: "); + 116 3 : fflush(stdout); 117 : - 118 2 : yafl_fiber_t *fiber = yafl_fiber_create(cycling_fiber_func, 16 * 1024, + 118 3 : yafl_fiber_t *fiber = yafl_fiber_create(cycling_fiber_func, 16 * 1024, 119 : YAFL_STACK_FLAGS_MALLOC); - 120 2 : assert(fiber != NULL); + 120 3 : assert(fiber != NULL); 121 : 122 : /* Cycle 1 */ - 123 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1); - 124 2 : assert(result == (void *)0x1); - 125 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 123 3 : void *result = yafl_fiber_resume(fiber, (void *)0x1); + 124 3 : assert(result == (void *)0x1); + 125 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 126 : 127 : /* Cycle 2 */ - 128 2 : result = yafl_fiber_resume(fiber, (void *)0x2); - 129 2 : assert(result == (void *)0x2); - 130 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 128 3 : result = yafl_fiber_resume(fiber, (void *)0x2); + 129 3 : assert(result == (void *)0x2); + 130 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 131 : 132 : /* Cycle 3 */ - 133 2 : result = yafl_fiber_resume(fiber, (void *)0x3); - 134 2 : assert(result == (void *)0x3); + 133 3 : result = yafl_fiber_resume(fiber, (void *)0x3); + 134 3 : assert(result == (void *)0x3); 135 : 136 : /* Cycle 4 */ - 137 2 : result = yafl_fiber_resume(fiber, (void *)0x4); - 138 2 : assert(result == (void *)0x4); + 137 3 : result = yafl_fiber_resume(fiber, (void *)0x4); + 138 3 : assert(result == (void *)0x4); 139 : 140 : /* Cycle 5 */ - 141 2 : result = yafl_fiber_resume(fiber, (void *)0x5); - 142 2 : assert(result == (void *)0x5); + 141 3 : result = yafl_fiber_resume(fiber, (void *)0x5); + 142 3 : assert(result == (void *)0x5); 143 : 144 : /* Final cycle - fiber completes */ - 145 2 : result = yafl_fiber_resume(fiber, (void *)0x6); - 146 2 : assert(result == (void *)0x6); - 147 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 145 3 : result = yafl_fiber_resume(fiber, (void *)0x6); + 146 3 : assert(result == (void *)0x6); + 147 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 148 : - 149 2 : yafl_fiber_destroy(fiber); + 149 3 : yafl_fiber_destroy(fiber); 150 : - 151 2 : printf("PASS\n"); - 152 2 : } + 151 3 : printf("PASS\n"); + 152 3 : } 153 : 154 : /* ======================================================================== 155 : * Test: Status Query 156 : * ======================================================================== */ 157 : - 158 2 : static void test_status_query(void) { - 159 2 : printf("test_status_query: "); - 160 2 : fflush(stdout); + 158 3 : static void test_status_query(void) { + 159 3 : printf("test_status_query: "); + 160 3 : fflush(stdout); 161 : - 162 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 162 3 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, 163 : YAFL_STACK_FLAGS_MALLOC); 164 : 165 : /* Check initial status */ - 166 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 166 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 167 : 168 : /* Check status of NULL */ - 169 2 : assert(yafl_fiber_status(NULL) == YAFL_FIBER_STATUS_ERR); + 169 3 : assert(yafl_fiber_status(NULL) == YAFL_FIBER_STATUS_ERR); 170 : 171 : /* Resume and check completion */ - 172 2 : yafl_fiber_resume(fiber, (void *)0x99); - 173 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 172 3 : yafl_fiber_resume(fiber, (void *)0x99); + 173 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 174 : - 175 2 : yafl_fiber_destroy(fiber); + 175 3 : yafl_fiber_destroy(fiber); 176 : - 177 2 : printf("PASS\n"); - 178 2 : } + 177 3 : printf("PASS\n"); + 178 3 : } 179 : 180 : /* ======================================================================== 181 : * Test: Watermark Measurement 182 : * ======================================================================== */ 183 : - 184 4 : static void *watermarked_fiber_func(void *arg) { + 184 6 : static void *watermarked_fiber_func(void *arg) { 185 : /* Allocate some stack space to trigger watermark usage */ - 186 4 : volatile char stack_buffer[1024]; + 186 6 : volatile char stack_buffer[1024]; 187 : /* Use memset to ensure compiler can't optimize the buffer away */ - 188 4 : memset((char *)stack_buffer, 0xAA, sizeof(stack_buffer)); - 189 8 : return arg; - 190 4 : } + 188 6 : memset((char *)stack_buffer, 0xAA, sizeof(stack_buffer)); + 189 12 : return arg; + 190 6 : } 191 : - 192 2 : static void test_watermark(void) { - 193 2 : printf("test_watermark_malloc: "); - 194 2 : fflush(stdout); + 192 3 : static void test_watermark(void) { + 193 3 : printf("test_watermark_malloc: "); + 194 3 : fflush(stdout); 195 : 196 : /* Test with malloc + watermark */ - 197 2 : yafl_fiber_t *fiber = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, + 197 3 : yafl_fiber_t *fiber = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, 198 : YAFL_STACK_FLAGS_MALLOC | YAFL_STACK_FLAGS_WATERMARK); - 199 2 : assert(fiber != NULL); + 199 3 : assert(fiber != NULL); 200 : 201 : /* Resume and check watermark */ - 202 2 : yafl_fiber_resume(fiber, NULL); - 203 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 202 3 : yafl_fiber_resume(fiber, NULL); + 203 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 204 : - 205 2 : size_t usage = yafl_fiber_stack_high_watermark(fiber); - 206 2 : assert(usage > 0); /* Should have used some stack */ + 205 3 : size_t usage = yafl_fiber_stack_high_watermark(fiber); + 206 3 : assert(usage > 0); /* Should have used some stack */ 207 : - 208 2 : yafl_fiber_destroy(fiber); + 208 3 : yafl_fiber_destroy(fiber); 209 : - 210 2 : printf("PASS\n"); + 210 3 : printf("PASS\n"); 211 : - 212 2 : printf("test_watermark_vmem: "); - 213 2 : fflush(stdout); + 212 3 : printf("test_watermark_vmem: "); + 213 3 : fflush(stdout); 214 : 215 : /* Test with vmem + watermark */ - 216 2 : yafl_fiber_t *fiber2 = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, + 216 3 : yafl_fiber_t *fiber2 = yafl_fiber_create(watermarked_fiber_func, 16 * 1024, 217 : YAFL_STACK_FLAGS_VMEM | YAFL_STACK_FLAGS_WATERMARK); - 218 2 : assert(fiber2 != NULL); + 218 3 : assert(fiber2 != NULL); 219 : - 220 2 : yafl_fiber_resume(fiber2, NULL); - 221 2 : assert(yafl_fiber_status(fiber2) == YAFL_FIBER_STATUS_COMPLETE); + 220 3 : yafl_fiber_resume(fiber2, NULL); + 221 3 : assert(yafl_fiber_status(fiber2) == YAFL_FIBER_STATUS_COMPLETE); 222 : - 223 2 : size_t usage2 = yafl_fiber_stack_high_watermark(fiber2); - 224 2 : assert(usage2 > 0); + 223 3 : size_t usage2 = yafl_fiber_stack_high_watermark(fiber2); + 224 3 : assert(usage2 > 0); 225 : - 226 2 : yafl_fiber_destroy(fiber2); + 226 3 : yafl_fiber_destroy(fiber2); 227 : - 228 2 : printf("PASS\n"); - 229 2 : } + 228 3 : printf("PASS\n"); + 229 3 : } 230 : 231 : /* ======================================================================== 232 : * Test: Watermark Without Flag 233 : * ======================================================================== */ 234 : - 235 2 : static void test_watermark_without_flag(void) { - 236 2 : printf("test_watermark_without_flag: "); - 237 2 : fflush(stdout); + 235 3 : static void test_watermark_without_flag(void) { + 236 3 : printf("test_watermark_without_flag: "); + 237 3 : fflush(stdout); 238 : - 239 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, + 239 3 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 16 * 1024, 240 : YAFL_STACK_FLAGS_MALLOC); - 241 2 : assert(fiber != NULL); + 241 3 : assert(fiber != NULL); 242 : 243 : /* Without watermark flag, should return 0 */ - 244 2 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); + 244 3 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); 245 : - 246 2 : yafl_fiber_resume(fiber, NULL); + 246 3 : yafl_fiber_resume(fiber, NULL); 247 : 248 : /* Still 0 because no watermark was applied */ - 249 2 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); + 249 3 : assert(yafl_fiber_stack_high_watermark(fiber) == 0); 250 : - 251 2 : yafl_fiber_destroy(fiber); + 251 3 : yafl_fiber_destroy(fiber); 252 : - 253 2 : printf("PASS\n"); - 254 2 : } + 253 3 : printf("PASS\n"); + 254 3 : } 255 : 256 : /* ======================================================================== 257 : * Test: NULL Fiber Pointer 258 : * ======================================================================== */ 259 : - 260 2 : static void test_null_fiber_pointer(void) { - 261 2 : printf("test_null_fiber_pointer: "); - 262 2 : fflush(stdout); + 260 3 : static void test_null_fiber_pointer(void) { + 261 3 : printf("test_null_fiber_pointer: "); + 262 3 : fflush(stdout); 263 : 264 : /* Resume NULL fiber */ - 265 2 : void *result = yafl_fiber_resume(NULL, (void *)0x1); - 266 2 : assert(result == NULL); + 265 3 : void *result = yafl_fiber_resume(NULL, (void *)0x1); + 266 3 : assert(result == NULL); 267 : 268 : /* Destroy NULL fiber (safe no-op) */ - 269 2 : yafl_fiber_destroy(NULL); + 269 3 : yafl_fiber_destroy(NULL); 270 : - 271 2 : printf("PASS\n"); - 272 2 : } + 271 3 : printf("PASS\n"); + 272 3 : } 273 : 274 : /* ======================================================================== 275 : * Test: NULL Entry Function 276 : * ======================================================================== */ 277 : - 278 2 : static void test_null_entry_function(void) { - 279 2 : printf("test_null_entry_function: "); - 280 2 : fflush(stdout); + 278 3 : static void test_null_entry_function(void) { + 279 3 : printf("test_null_entry_function: "); + 280 3 : fflush(stdout); 281 : - 282 2 : yafl_fiber_t *fiber = yafl_fiber_create(NULL, 16 * 1024, + 282 3 : yafl_fiber_t *fiber = yafl_fiber_create(NULL, 16 * 1024, 283 : YAFL_STACK_FLAGS_MALLOC); - 284 2 : assert(fiber == NULL); + 284 3 : assert(fiber == NULL); 285 : - 286 2 : printf("PASS\n"); - 287 2 : } + 286 3 : printf("PASS\n"); + 287 3 : } 288 : 289 : /* ======================================================================== 290 : * Test: Default Stack Size 291 : * ======================================================================== */ 292 : - 293 2 : static void test_default_stack_size(void) { - 294 2 : printf("test_default_stack_size: "); - 295 2 : fflush(stdout); + 293 3 : static void test_default_stack_size(void) { + 294 3 : printf("test_default_stack_size: "); + 295 3 : fflush(stdout); 296 : 297 : /* Create with 0 stack size (uses default) */ - 298 2 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 0, + 298 3 : yafl_fiber_t *fiber = yafl_fiber_create(simple_fiber_func, 0, 299 : YAFL_STACK_FLAGS_MALLOC); - 300 2 : assert(fiber != NULL); + 300 3 : assert(fiber != NULL); 301 : - 302 2 : yafl_fiber_resume(fiber, (void *)0x42); - 303 2 : yafl_fiber_destroy(fiber); + 302 3 : yafl_fiber_resume(fiber, (void *)0x42); + 303 3 : yafl_fiber_destroy(fiber); 304 : - 305 2 : printf("PASS\n"); - 306 2 : } + 305 3 : printf("PASS\n"); + 306 3 : } 307 : 308 : /* ======================================================================== 309 : * Main 310 : * ======================================================================== */ 311 : - 312 2 : int main(void) { - 313 2 : printf("Running YAFL basic tests...\n"); + 312 3 : int main(void) { + 313 3 : printf("Running YAFL basic tests...\n"); 314 : - 315 2 : test_simple_execution(); - 316 2 : test_flag_combinations(); - 317 2 : test_suspend_resume_cycles(); - 318 2 : test_status_query(); - 319 2 : test_watermark(); - 320 2 : test_watermark_without_flag(); - 321 2 : test_null_fiber_pointer(); - 322 2 : test_null_entry_function(); - 323 2 : test_default_stack_size(); + 315 3 : test_simple_execution(); + 316 3 : test_flag_combinations(); + 317 3 : test_suspend_resume_cycles(); + 318 3 : test_status_query(); + 319 3 : test_watermark(); + 320 3 : test_watermark_without_flag(); + 321 3 : test_null_fiber_pointer(); + 322 3 : test_null_entry_function(); + 323 3 : test_default_stack_size(); 324 : - 325 2 : printf("\nAll tests passed!\n"); - 326 2 : return 0; + 325 3 : printf("\nAll tests passed!\n"); + 326 3 : return 0; 327 : } diff --git a/docs/coverage/tests/test_yafl_guard.c.func-c.html b/docs/coverage/tests/test_yafl_guard.c.func-c.html index 5479975..7d5bd7c 100644 --- a/docs/coverage/tests/test_yafl_guard.c.func-c.html +++ b/docs/coverage/tests/test_yafl_guard.c.func-c.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - - + + + - + @@ -61,6 +61,7 @@ + @@ -68,6 +69,7 @@ + @@ -75,6 +77,7 @@ + @@ -82,11 +85,13 @@ + - + + @@ -95,7 +100,7 @@
Current view:top level - tests - test_yafl_guard.c (source / functions)top level - tests - test_yafl_guard.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines:32.5 %772534.1 %8228
Test Date:Generated2026-04-28 13:50:18 Functions: 25.0 %Hit count Sort by function hit count
guard_test_fiber0
overflow_stack0
run_child_test0
main14
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_guard.c.func.html b/docs/coverage/tests/test_yafl_guard.c.func.html index f456f8b..6732e17 100644 --- a/docs/coverage/tests/test_yafl_guard.c.func.html +++ b/docs/coverage/tests/test_yafl_guard.c.func.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - - + + + - + @@ -61,6 +61,7 @@ + @@ -68,11 +69,13 @@ + - + + @@ -82,6 +85,7 @@ + @@ -89,13 +93,14 @@ +
Current view:top level - tests - test_yafl_guard.c (source / functions)top level - tests - test_yafl_guard.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines:32.5 %772534.1 %8228
Test Date:Generated2026-04-28 13:50:18 Functions: 25.0 %Hit count Sort by function hit count
guard_test_fiber0
main14
0
run_child_test0

- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_guard.c.gcov.html b/docs/coverage/tests/test_yafl_guard.c.gcov.html index 43892bd..bc4925f 100644 --- a/docs/coverage/tests/test_yafl_guard.c.gcov.html +++ b/docs/coverage/tests/test_yafl_guard.c.gcov.html @@ -19,7 +19,7 @@ - + @@ -28,16 +28,16 @@ - + - - - + + + - + @@ -86,7 +86,7 @@ 24 : #endif 25 : 26 : /* Recursive function to overflow the stack */ - 27 0 : static void overflow_stack(int depth) { + 27 0 : static void overflow_stack(int depth) { 28 0 : volatile char buffer[128]; 29 : 30 : /* Touch the buffer to prevent optimization */ @@ -98,7 +98,7 @@ 36 : /* Recurse to build up stack usage to trigger guard page */ 37 0 : if (depth < 250) { 38 0 : overflow_stack(depth + 1); - 39 : } + 39 0 : } 40 0 : } 41 : 42 0 : static void *guard_test_fiber(void *data) { @@ -139,16 +139,16 @@ 77 0 : fflush(stderr); 78 0 : yafl_fiber_destroy(fiber); 79 0 : return 1; - 80 : } + 80 0 : } 81 : - 82 1 : int main(int argc, char *argv[]) { - 83 1 : fprintf(stderr, "=== yafl Guard Page Test ===\n"); - 84 1 : fflush(stderr); + 82 4 : int main(int argc, char *argv[]) { + 83 4 : fprintf(stderr, "=== yafl Guard Page Test ===\n"); + 84 4 : fflush(stderr); 85 : 86 : /* argc and argv only used on Windows */ 87 : #ifndef _WIN32 - 88 1 : (void)argc; - 89 1 : (void)argv; + 88 4 : (void)argc; + 89 4 : (void)argv; 90 : #endif 91 : 92 : #ifdef _WIN32 @@ -210,49 +210,49 @@ 148 : 149 : #else 150 : /* POSIX: use fork */ - 151 1 : fprintf(stderr, "[main] spawning child process\n"); - 152 1 : fflush(stderr); + 151 4 : fprintf(stderr, "[main] spawning child process\n"); + 152 4 : fflush(stderr); 153 : - 154 1 : pid_t pid = fork(); - 155 1 : if (pid == -1) { - 156 0 : fprintf(stderr, "[main] ERROR: fork failed\n"); + 154 4 : pid_t pid = fork(); + 155 4 : if (pid == -1) { + 156 0 : fprintf(stderr, "[main] ERROR: fork failed\n"); 157 0 : fflush(stderr); 158 0 : return 1; 159 : } 160 : - 161 1 : if (pid == 0) { + 161 4 : if (pid == 0) { 162 : /* Child process - run test and exit */ - 163 0 : exit(run_child_test()); + 163 0 : exit(run_child_test()); 164 : } 165 : 166 : /* Parent process - wait for child */ - 167 1 : fprintf(stderr, "[main] waiting for child process (PID: %d)\n", (int)pid); - 168 1 : fflush(stderr); + 167 4 : fprintf(stderr, "[main] waiting for child process (PID: %d)\n", (int)pid); + 168 4 : fflush(stderr); 169 : - 170 1 : int status; - 171 1 : pid_t result = waitpid(pid, &status, 0); - 172 1 : if (result == -1) { - 173 0 : fprintf(stderr, "[main] ERROR: waitpid failed\n"); + 170 4 : int status; + 171 4 : pid_t result = waitpid(pid, &status, 0); + 172 4 : if (result == -1) { + 173 0 : fprintf(stderr, "[main] ERROR: waitpid failed\n"); 174 0 : fflush(stderr); 175 0 : return 1; 176 : } 177 : 178 : /* Check how the child terminated */ - 179 1 : if (WIFSIGNALED(status)) { - 180 1 : int sig = WTERMSIG(status); - 181 1 : fprintf(stderr, "[main] child killed by signal %d\n", sig); - 182 1 : fflush(stderr); + 179 4 : if (WIFSIGNALED(status)) { + 180 4 : int sig = WTERMSIG(status); + 181 4 : fprintf(stderr, "[main] child killed by signal %d\n", sig); + 182 4 : fflush(stderr); 183 : - 184 : /* Expect SIGSEGV (11) or SIGBUS (7) for guard page fault */ - 185 1 : if (sig == SIGSEGV || sig == SIGBUS) { - 186 1 : fprintf(stderr, "[main] guard page successfully detected stack overflow\n"); - 187 1 : fflush(stderr); - 188 : } else { - 189 0 : fprintf(stderr, "[main] FAIL: child killed by unexpected signal %d\n", sig); + 184 : /* Expect SIGSEGV (11) or SIGBUS (7) SIGILL (4) for guard page fault */ + 185 4 : if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL) { + 186 4 : fprintf(stderr, "[main] guard page successfully detected stack overflow\n"); + 187 4 : fflush(stderr); + 188 4 : } else { + 189 0 : fprintf(stderr, "[main] FAIL: child killed by unexpected signal %d\n", sig); 190 0 : fflush(stderr); 191 0 : return 1; 192 : } - 193 0 : } else if (WIFEXITED(status)) { + 193 4 : } else if (WIFEXITED(status)) { 194 0 : int exit_code = WEXITSTATUS(status); 195 0 : fprintf(stderr, "[main] child exited with code %d\n", exit_code); 196 0 : fflush(stderr); @@ -262,7 +262,7 @@ 200 0 : fflush(stderr); 201 0 : return 1; 202 : } - 203 : } else { + 203 0 : } else { 204 0 : fprintf(stderr, "[main] FAIL: unexpected child termination\n"); 205 0 : fflush(stderr); 206 0 : return 1; @@ -270,10 +270,10 @@ 208 : 209 : #endif 210 : - 211 1 : fprintf(stderr, "\nPASS: Guard page detected stack overflow\n"); - 212 1 : fflush(stderr); - 213 1 : return 0; - 214 : } + 211 4 : fprintf(stderr, "\nPASS: Guard page detected stack overflow\n"); + 212 4 : fflush(stderr); + 213 4 : return 0; + 214 4 : } @@ -282,7 +282,7 @@
Current view:top level - tests - test_yafl_guard.c (source / functions)top level - tests - test_yafl_guard.c (source / functions) Coverage
Test:Generatedyafl Code Coverage Lines:32.5 %772534.1 %8228
Test Date:Generated2026-04-28 13:50:18 Functions: 25.0 %
- +
Generated by: LCOV version 2.0-1
Generated by: LCOV version 2.4-0

diff --git a/docs/coverage/tests/test_yafl_many.c.func-c.html b/docs/coverage/tests/test_yafl_many.c.func-c.html index c20f298..0077423 100644 --- a/docs/coverage/tests/test_yafl_many.c.func-c.html +++ b/docs/coverage/tests/test_yafl_many.c.func-c.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -66,7 +66,7 @@ main - 2 + 3 @@ -74,7 +74,7 @@ fiber_entry - 200 + 300 diff --git a/docs/coverage/tests/test_yafl_many.c.func.html b/docs/coverage/tests/test_yafl_many.c.func.html index 63271ac..c78779d 100644 --- a/docs/coverage/tests/test_yafl_many.c.func.html +++ b/docs/coverage/tests/test_yafl_many.c.func.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -66,7 +66,7 @@ fiber_entry - 200 + 300 @@ -74,7 +74,7 @@ main - 2 + 3 diff --git a/docs/coverage/tests/test_yafl_many.c.gcov.html b/docs/coverage/tests/test_yafl_many.c.gcov.html index 9e89e0c..dc970ae 100644 --- a/docs/coverage/tests/test_yafl_many.c.gcov.html +++ b/docs/coverage/tests/test_yafl_many.c.gcov.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -85,111 +85,111 @@ 23 : static int counters[NUM_FIBERS]; 24 : static yafl_fiber_t *fibers[NUM_FIBERS]; 25 : - 26 200 : static void *fiber_entry(void *data) { - 27 200 : int id = (int)(uintptr_t)data; + 26 300 : static void *fiber_entry(void *data) { + 27 300 : int id = (int)(uintptr_t)data; 28 : - 29 200 : fprintf(stderr, "[fiber %d] entered\n", id); - 30 200 : fflush(stderr); + 29 300 : fprintf(stderr, "[fiber %d] entered\n", id); + 30 300 : fflush(stderr); 31 : - 32 2200 : for (int i = 0; i < ITERATIONS; i++) { - 33 2000 : fprintf(stderr, "[fiber %d] iteration %d/%d\n", id, i + 1, ITERATIONS); - 34 2000 : fflush(stderr); + 32 3300 : for (int i = 0; i < ITERATIONS; i++) { + 33 3000 : fprintf(stderr, "[fiber %d] iteration %d/%d\n", id, i + 1, ITERATIONS); + 34 3000 : fflush(stderr); 35 : - 36 2000 : counters[id]++; + 36 3000 : counters[id]++; 37 : - 38 2000 : fprintf(stderr, "[fiber %d] suspending\n", id); - 39 2000 : fflush(stderr); - 40 2000 : yafl_fiber_suspend(NULL); + 38 3000 : fprintf(stderr, "[fiber %d] suspending\n", id); + 39 3000 : fflush(stderr); + 40 3000 : yafl_fiber_suspend(NULL); 41 : - 42 2000 : fprintf(stderr, "[fiber %d] resumed\n", id); - 43 2000 : fflush(stderr); - 44 2000 : } + 42 3000 : fprintf(stderr, "[fiber %d] resumed\n", id); + 43 3000 : fflush(stderr); + 44 3000 : } 45 : - 46 200 : fprintf(stderr, "[fiber %d] finishing with result %d\n", id, id + 1000); - 47 200 : fflush(stderr); - 48 200 : return (void *)(uintptr_t)(id + 1000); - 49 200 : } + 46 300 : fprintf(stderr, "[fiber %d] finishing with result %d\n", id, id + 1000); + 47 300 : fflush(stderr); + 48 300 : return (void *)(uintptr_t)(id + 1000); + 49 300 : } 50 : - 51 2 : int main(void) { - 52 2 : fprintf(stderr, "=== yafl Many Fibers Test ===\n"); - 53 2 : fprintf(stderr, "[main] testing with %d fibers, %d iterations each\n", NUM_FIBERS, ITERATIONS); - 54 2 : fflush(stderr); + 51 3 : int main(void) { + 52 3 : fprintf(stderr, "=== yafl Many Fibers Test ===\n"); + 53 3 : fprintf(stderr, "[main] testing with %d fibers, %d iterations each\n", NUM_FIBERS, ITERATIONS); + 54 3 : fflush(stderr); 55 : 56 : /* Create 100 fibers */ - 57 2 : fprintf(stderr, "[main] creating %d fibers\n", NUM_FIBERS); - 58 2 : fflush(stderr); - 59 202 : for (int i = 0; i < NUM_FIBERS; i++) { - 60 200 : counters[i] = 0; - 61 200 : fibers[i] = yafl_fiber_create(fiber_entry, 24 * 1024, YAFL_STACK_FLAGS_VMEM); - 62 200 : assert(fibers[i] != NULL); + 57 3 : fprintf(stderr, "[main] creating %d fibers\n", NUM_FIBERS); + 58 3 : fflush(stderr); + 59 303 : for (int i = 0; i < NUM_FIBERS; i++) { + 60 300 : counters[i] = 0; + 61 300 : fibers[i] = yafl_fiber_create(fiber_entry, 24 * 1024, YAFL_STACK_FLAGS_VMEM); + 62 300 : assert(fibers[i] != NULL); 63 : 64 : /* Test status on created fiber */ - 65 200 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); - 66 200 : assert(status == YAFL_FIBER_STATUS_SUSPENDED); + 65 300 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); + 66 300 : assert(status == YAFL_FIBER_STATUS_SUSPENDED); 67 : - 68 200 : if (i == 0 || i == NUM_FIBERS - 1) { - 69 4 : fprintf(stderr, "[main] fiber %d: created, status=%d\n", i, status); - 70 4 : fflush(stderr); - 71 4 : } - 72 200 : } - 73 2 : fprintf(stderr, "[main] all %d fibers created successfully\n", NUM_FIBERS); - 74 2 : fflush(stderr); + 68 300 : if (i == 0 || i == NUM_FIBERS - 1) { + 69 6 : fprintf(stderr, "[main] fiber %d: created, status=%d\n", i, status); + 70 6 : fflush(stderr); + 71 6 : } + 72 300 : } + 73 3 : fprintf(stderr, "[main] all %d fibers created successfully\n", NUM_FIBERS); + 74 3 : fflush(stderr); 75 : 76 : /* Round-robin scheduling until all done */ - 77 2 : fprintf(stderr, "[main] starting round-robin scheduling\n"); - 78 2 : fflush(stderr); + 77 3 : fprintf(stderr, "[main] starting round-robin scheduling\n"); + 78 3 : fflush(stderr); 79 : - 80 2 : int round = 0; - 81 2 : bool all_done = false; - 82 26 : while (!all_done) { - 83 24 : all_done = true; - 84 24 : int active_count = 0; + 80 3 : int round = 0; + 81 3 : bool all_done = false; + 82 39 : while (!all_done) { + 83 36 : all_done = true; + 84 36 : int active_count = 0; 85 : - 86 2424 : for (int i = 0; i < NUM_FIBERS; i++) { - 87 2400 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); - 88 2400 : if (status != YAFL_FIBER_STATUS_COMPLETE) { - 89 2200 : yafl_fiber_resume(fibers[i], (void *)(uintptr_t)i); - 90 2200 : all_done = false; - 91 2200 : active_count++; - 92 2200 : } - 93 2400 : } + 86 3636 : for (int i = 0; i < NUM_FIBERS; i++) { + 87 3600 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); + 88 3600 : if (status != YAFL_FIBER_STATUS_COMPLETE) { + 89 3300 : yafl_fiber_resume(fibers[i], (void *)(uintptr_t)i); + 90 3300 : all_done = false; + 91 3300 : active_count++; + 92 3300 : } + 93 3600 : } 94 : - 95 24 : round++; - 96 24 : if (round <= 3 || all_done) { - 97 8 : fprintf(stderr, "[main] round %d: %d fibers still active\n", round, active_count); - 98 8 : fflush(stderr); - 99 8 : } - 100 24 : } + 95 36 : round++; + 96 36 : if (round <= 3 || all_done) { + 97 12 : fprintf(stderr, "[main] round %d: %d fibers still active\n", round, active_count); + 98 12 : fflush(stderr); + 99 12 : } + 100 36 : } 101 : - 102 2 : fprintf(stderr, "[main] all fibers finished after %d rounds\n", round); - 103 2 : fflush(stderr); + 102 3 : fprintf(stderr, "[main] all fibers finished after %d rounds\n", round); + 103 3 : fflush(stderr); 104 : 105 : /* Verify all fibers ran exactly ITERATIONS times */ - 106 2 : fprintf(stderr, "[main] verifying all fibers completed %d iterations\n", ITERATIONS); - 107 2 : fflush(stderr); + 106 3 : fprintf(stderr, "[main] verifying all fibers completed %d iterations\n", ITERATIONS); + 107 3 : fflush(stderr); 108 : - 109 202 : for (int i = 0; i < NUM_FIBERS; i++) { - 110 200 : assert(counters[i] == ITERATIONS); + 109 303 : for (int i = 0; i < NUM_FIBERS; i++) { + 110 300 : assert(counters[i] == ITERATIONS); 111 : - 112 200 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); - 113 200 : assert(status == YAFL_FIBER_STATUS_COMPLETE); + 112 300 : yafl_fiber_status_t status = yafl_fiber_status(fibers[i]); + 113 300 : assert(status == YAFL_FIBER_STATUS_COMPLETE); 114 : - 115 200 : if (i == 0 || i == NUM_FIBERS - 1) { - 116 4 : fprintf(stderr, "[main] fiber %d: counter=%d, status=%d\n", i, counters[i], status); - 117 4 : fflush(stderr); - 118 4 : } + 115 300 : if (i == 0 || i == NUM_FIBERS - 1) { + 116 6 : fprintf(stderr, "[main] fiber %d: counter=%d, status=%d\n", i, counters[i], status); + 117 6 : fflush(stderr); + 118 6 : } 119 : - 120 200 : yafl_fiber_destroy(fibers[i]); - 121 200 : } + 120 300 : yafl_fiber_destroy(fibers[i]); + 121 300 : } 122 : - 123 2 : fprintf(stderr, "[main] all fibers verified and destroyed\n"); - 124 2 : fflush(stderr); + 123 3 : fprintf(stderr, "[main] all fibers verified and destroyed\n"); + 124 3 : fflush(stderr); 125 : - 126 2 : fprintf(stderr, "\nPASS: %d fibers x %d iterations = %d total context switches\n", NUM_FIBERS, + 126 3 : fprintf(stderr, "\nPASS: %d fibers x %d iterations = %d total context switches\n", NUM_FIBERS, 127 : ITERATIONS, NUM_FIBERS * ITERATIONS); - 128 2 : fflush(stderr); - 129 2 : return 0; - 130 2 : } + 128 3 : fflush(stderr); + 129 3 : return 0; + 130 3 : } diff --git a/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html b/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html index b48bc56..db20129 100644 --- a/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html +++ b/docs/coverage/tests/test_yafl_suspend_resume.c.func-c.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -66,7 +66,7 @@ conditional_suspend_fiber_func - 2 + 3 @@ -74,7 +74,7 @@ echo_fiber_func - 2 + 3 @@ -82,7 +82,7 @@ main - 2 + 3 @@ -90,7 +90,7 @@ multi_suspend_fiber_func - 2 + 3 @@ -98,7 +98,7 @@ null_data_fiber_func - 2 + 3 @@ -106,7 +106,7 @@ test_bidirectional_data_passing - 2 + 3 @@ -114,7 +114,7 @@ test_conditional_suspension - 2 + 3 @@ -122,7 +122,7 @@ test_multiple_suspend_points - 2 + 3 @@ -130,7 +130,7 @@ test_null_data_passing - 2 + 3 @@ -138,7 +138,7 @@ test_vmem_suspend_resume - 2 + 3 @@ -146,7 +146,7 @@ vmem_test_func - 2 + 3 diff --git a/docs/coverage/tests/test_yafl_suspend_resume.c.func.html b/docs/coverage/tests/test_yafl_suspend_resume.c.func.html index 78788e4..d30db38 100644 --- a/docs/coverage/tests/test_yafl_suspend_resume.c.func.html +++ b/docs/coverage/tests/test_yafl_suspend_resume.c.func.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -66,7 +66,7 @@ conditional_suspend_fiber_func - 2 + 3 @@ -74,7 +74,7 @@ echo_fiber_func - 2 + 3 @@ -82,7 +82,7 @@ main - 2 + 3 @@ -90,7 +90,7 @@ multi_suspend_fiber_func - 2 + 3 @@ -98,7 +98,7 @@ null_data_fiber_func - 2 + 3 @@ -106,7 +106,7 @@ test_bidirectional_data_passing - 2 + 3 @@ -114,7 +114,7 @@ test_conditional_suspension - 2 + 3 @@ -122,7 +122,7 @@ test_multiple_suspend_points - 2 + 3 @@ -130,7 +130,7 @@ test_null_data_passing - 2 + 3 @@ -138,7 +138,7 @@ test_vmem_suspend_resume - 2 + 3 @@ -146,7 +146,7 @@ vmem_test_func - 2 + 3 diff --git a/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html b/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html index ab2951b..abfcab5 100644 --- a/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html +++ b/docs/coverage/tests/test_yafl_suspend_resume.c.gcov.html @@ -37,7 +37,7 @@ Test Date: - 2026-04-28 13:42:34 + 2026-04-28 13:50:18 Functions: 100.0 % @@ -78,201 +78,201 @@ 16 : * Test: Bidirectional Data Passing 17 : * ======================================================================== */ 18 : - 19 2 : static void *echo_fiber_func(void *arg) { + 19 3 : static void *echo_fiber_func(void *arg) { 20 : /* Fiber receives initial arg, suspends with it */ - 21 2 : arg = yafl_fiber_suspend(arg); + 21 3 : arg = yafl_fiber_suspend(arg); 22 : 23 : /* Receives new arg, suspends with it */ - 24 2 : arg = yafl_fiber_suspend(arg); + 24 3 : arg = yafl_fiber_suspend(arg); 25 : 26 : /* Receives final arg, returns it */ - 27 2 : return arg; + 27 3 : return arg; 28 : } 29 : - 30 2 : static void test_bidirectional_data_passing(void) { - 31 2 : printf("test_bidirectional_data_passing: "); - 32 2 : fflush(stdout); + 30 3 : static void test_bidirectional_data_passing(void) { + 31 3 : printf("test_bidirectional_data_passing: "); + 32 3 : fflush(stdout); 33 : - 34 2 : yafl_fiber_t *fiber = yafl_fiber_create(echo_fiber_func, 16 * 1024, + 34 3 : yafl_fiber_t *fiber = yafl_fiber_create(echo_fiber_func, 16 * 1024, 35 : YAFL_STACK_FLAGS_MALLOC); - 36 2 : assert(fiber != NULL); + 36 3 : assert(fiber != NULL); 37 : 38 : /* First resume: send 0x1111, receive 0x1111 back */ - 39 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); - 40 2 : assert(result == (void *)0x1111); - 41 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 39 3 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); + 40 3 : assert(result == (void *)0x1111); + 41 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 42 : 43 : /* Second resume: send 0x2222, receive 0x2222 back */ - 44 2 : result = yafl_fiber_resume(fiber, (void *)0x2222); - 45 2 : assert(result == (void *)0x2222); - 46 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 44 3 : result = yafl_fiber_resume(fiber, (void *)0x2222); + 45 3 : assert(result == (void *)0x2222); + 46 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); 47 : 48 : /* Third resume: send 0x3333, receive 0x3333 back (fiber completes) */ - 49 2 : result = yafl_fiber_resume(fiber, (void *)0x3333); - 50 2 : assert(result == (void *)0x3333); - 51 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 49 3 : result = yafl_fiber_resume(fiber, (void *)0x3333); + 50 3 : assert(result == (void *)0x3333); + 51 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 52 : - 53 2 : yafl_fiber_destroy(fiber); + 53 3 : yafl_fiber_destroy(fiber); 54 : - 55 2 : printf("PASS\n"); - 56 2 : } + 55 3 : printf("PASS\n"); + 56 3 : } 57 : 58 : /* ======================================================================== 59 : * Test: Multiple Suspend Points 60 : * ======================================================================== */ 61 : - 62 2 : static void *multi_suspend_fiber_func(void *arg) { - 63 22 : for (int i = 0; i < 10; i++) { - 64 20 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x1000 + i)); - 65 20 : } - 66 2 : return arg; + 62 3 : static void *multi_suspend_fiber_func(void *arg) { + 63 33 : for (int i = 0; i < 10; i++) { + 64 30 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x1000 + i)); + 65 30 : } + 66 3 : return arg; 67 : } 68 : - 69 2 : static void test_multiple_suspend_points(void) { - 70 2 : printf("test_multiple_suspend_points: "); - 71 2 : fflush(stdout); + 69 3 : static void test_multiple_suspend_points(void) { + 70 3 : printf("test_multiple_suspend_points: "); + 71 3 : fflush(stdout); 72 : - 73 2 : yafl_fiber_t *fiber = yafl_fiber_create(multi_suspend_fiber_func, 16 * 1024, + 73 3 : yafl_fiber_t *fiber = yafl_fiber_create(multi_suspend_fiber_func, 16 * 1024, 74 : YAFL_STACK_FLAGS_MALLOC); - 75 2 : assert(fiber != NULL); + 75 3 : assert(fiber != NULL); 76 : - 77 22 : for (int i = 0; i < 10; i++) { - 78 20 : void *result = yafl_fiber_resume(fiber, (void *)(uintptr_t)(0x2000 + i)); - 79 20 : assert(result == (void *)(uintptr_t)(0x1000 + i)); - 80 20 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); - 81 20 : } + 77 33 : for (int i = 0; i < 10; i++) { + 78 30 : void *result = yafl_fiber_resume(fiber, (void *)(uintptr_t)(0x2000 + i)); + 79 30 : assert(result == (void *)(uintptr_t)(0x1000 + i)); + 80 30 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_SUSPENDED); + 81 30 : } 82 : 83 : /* Final resume to complete - should return the last arg passed to resume */ - 84 2 : void *final = yafl_fiber_resume(fiber, (void *)0x3000); - 85 2 : assert(final == (void *)0x3000); - 86 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 84 3 : void *final = yafl_fiber_resume(fiber, (void *)0x3000); + 85 3 : assert(final == (void *)0x3000); + 86 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 87 : - 88 2 : yafl_fiber_destroy(fiber); + 88 3 : yafl_fiber_destroy(fiber); 89 : - 90 2 : printf("PASS\n"); - 91 2 : } + 90 3 : printf("PASS\n"); + 91 3 : } 92 : 93 : /* ======================================================================== 94 : * Test: Conditional Suspension 95 : * ======================================================================== */ 96 : - 97 2 : static void *conditional_suspend_fiber_func(void *arg) { - 98 12 : for (int i = 0; i < 5; i++) { - 99 10 : if (i % 2 == 0) { - 100 6 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x100 + i)); - 101 6 : } - 102 10 : } - 103 2 : return arg; + 97 3 : static void *conditional_suspend_fiber_func(void *arg) { + 98 18 : for (int i = 0; i < 5; i++) { + 99 15 : if (i % 2 == 0) { + 100 9 : arg = yafl_fiber_suspend((void *)(uintptr_t)(0x100 + i)); + 101 9 : } + 102 15 : } + 103 3 : return arg; 104 : } 105 : - 106 2 : static void test_conditional_suspension(void) { - 107 2 : printf("test_conditional_suspension: "); - 108 2 : fflush(stdout); + 106 3 : static void test_conditional_suspension(void) { + 107 3 : printf("test_conditional_suspension: "); + 108 3 : fflush(stdout); 109 : - 110 2 : yafl_fiber_t *fiber = yafl_fiber_create(conditional_suspend_fiber_func, 16 * 1024, + 110 3 : yafl_fiber_t *fiber = yafl_fiber_create(conditional_suspend_fiber_func, 16 * 1024, 111 : YAFL_STACK_FLAGS_MALLOC); - 112 2 : assert(fiber != NULL); + 112 3 : assert(fiber != NULL); 113 : 114 : /* Suspend at i=0 */ - 115 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1); - 116 2 : assert(result == (void *)0x100); + 115 3 : void *result = yafl_fiber_resume(fiber, (void *)0x1); + 116 3 : assert(result == (void *)0x100); 117 : 118 : /* Suspend at i=2 */ - 119 2 : result = yafl_fiber_resume(fiber, (void *)0x2); - 120 2 : assert(result == (void *)0x102); + 119 3 : result = yafl_fiber_resume(fiber, (void *)0x2); + 120 3 : assert(result == (void *)0x102); 121 : 122 : /* Suspend at i=4 */ - 123 2 : result = yafl_fiber_resume(fiber, (void *)0x3); - 124 2 : assert(result == (void *)0x104); + 123 3 : result = yafl_fiber_resume(fiber, (void *)0x3); + 124 3 : assert(result == (void *)0x104); 125 : 126 : /* Complete */ - 127 2 : result = yafl_fiber_resume(fiber, (void *)0x4); - 128 2 : assert(result == (void *)0x4); - 129 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 127 3 : result = yafl_fiber_resume(fiber, (void *)0x4); + 128 3 : assert(result == (void *)0x4); + 129 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 130 : - 131 2 : yafl_fiber_destroy(fiber); + 131 3 : yafl_fiber_destroy(fiber); 132 : - 133 2 : printf("PASS\n"); - 134 2 : } + 133 3 : printf("PASS\n"); + 134 3 : } 135 : 136 : /* ======================================================================== 137 : * Test: NULL Data Passing 138 : * ======================================================================== */ 139 : - 140 2 : static void *null_data_fiber_func(void *arg) { - 141 2 : assert(arg == NULL); /* First resume passes NULL */ - 142 2 : arg = yafl_fiber_suspend(NULL); - 143 2 : assert(arg == NULL); /* Second resume passes NULL */ - 144 2 : return arg; + 140 3 : static void *null_data_fiber_func(void *arg) { + 141 3 : assert(arg == NULL); /* First resume passes NULL */ + 142 3 : arg = yafl_fiber_suspend(NULL); + 143 3 : assert(arg == NULL); /* Second resume passes NULL */ + 144 3 : return arg; 145 : } 146 : - 147 2 : static void test_null_data_passing(void) { - 148 2 : printf("test_null_data_passing: "); - 149 2 : fflush(stdout); + 147 3 : static void test_null_data_passing(void) { + 148 3 : printf("test_null_data_passing: "); + 149 3 : fflush(stdout); 150 : - 151 2 : yafl_fiber_t *fiber = yafl_fiber_create(null_data_fiber_func, 16 * 1024, + 151 3 : yafl_fiber_t *fiber = yafl_fiber_create(null_data_fiber_func, 16 * 1024, 152 : YAFL_STACK_FLAGS_MALLOC); - 153 2 : assert(fiber != NULL); + 153 3 : assert(fiber != NULL); 154 : - 155 2 : void *result = yafl_fiber_resume(fiber, NULL); - 156 2 : assert(result == NULL); + 155 3 : void *result = yafl_fiber_resume(fiber, NULL); + 156 3 : assert(result == NULL); 157 : - 158 2 : result = yafl_fiber_resume(fiber, NULL); - 159 2 : assert(result == NULL); + 158 3 : result = yafl_fiber_resume(fiber, NULL); + 159 3 : assert(result == NULL); 160 : - 161 2 : yafl_fiber_destroy(fiber); + 161 3 : yafl_fiber_destroy(fiber); 162 : - 163 2 : printf("PASS\n"); - 164 2 : } + 163 3 : printf("PASS\n"); + 164 3 : } 165 : 166 : /* ======================================================================== 167 : * Test: VMEM Allocation with Suspend/Resume 168 : * ======================================================================== */ 169 : - 170 2 : static void *vmem_test_func(void *arg) { - 171 2 : arg = yafl_fiber_suspend((void *)0xAAAA); - 172 2 : arg = yafl_fiber_suspend((void *)0xBBBB); - 173 2 : return arg; + 170 3 : static void *vmem_test_func(void *arg) { + 171 3 : arg = yafl_fiber_suspend((void *)0xAAAA); + 172 3 : arg = yafl_fiber_suspend((void *)0xBBBB); + 173 3 : return arg; 174 : } 175 : - 176 2 : static void test_vmem_suspend_resume(void) { - 177 2 : printf("test_vmem_suspend_resume: "); - 178 2 : fflush(stdout); + 176 3 : static void test_vmem_suspend_resume(void) { + 177 3 : printf("test_vmem_suspend_resume: "); + 178 3 : fflush(stdout); 179 : - 180 2 : yafl_fiber_t *fiber = yafl_fiber_create(vmem_test_func, 16 * 1024, + 180 3 : yafl_fiber_t *fiber = yafl_fiber_create(vmem_test_func, 16 * 1024, 181 : YAFL_STACK_FLAGS_VMEM); - 182 2 : assert(fiber != NULL); + 182 3 : assert(fiber != NULL); 183 : - 184 2 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); - 185 2 : assert(result == (void *)0xAAAA); + 184 3 : void *result = yafl_fiber_resume(fiber, (void *)0x1111); + 185 3 : assert(result == (void *)0xAAAA); 186 : - 187 2 : result = yafl_fiber_resume(fiber, (void *)0x2222); - 188 2 : assert(result == (void *)0xBBBB); + 187 3 : result = yafl_fiber_resume(fiber, (void *)0x2222); + 188 3 : assert(result == (void *)0xBBBB); 189 : - 190 2 : result = yafl_fiber_resume(fiber, (void *)0x3333); - 191 2 : assert(result == (void *)0x3333); - 192 2 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); + 190 3 : result = yafl_fiber_resume(fiber, (void *)0x3333); + 191 3 : assert(result == (void *)0x3333); + 192 3 : assert(yafl_fiber_status(fiber) == YAFL_FIBER_STATUS_COMPLETE); 193 : - 194 2 : yafl_fiber_destroy(fiber); + 194 3 : yafl_fiber_destroy(fiber); 195 : - 196 2 : printf("PASS\n"); - 197 2 : } + 196 3 : printf("PASS\n"); + 197 3 : } 198 : 199 : /* ======================================================================== 200 : * Main 201 : * ======================================================================== */ 202 : - 203 2 : int main(void) { - 204 2 : printf("Running YAFL suspend/resume tests...\n"); + 203 3 : int main(void) { + 204 3 : printf("Running YAFL suspend/resume tests...\n"); 205 : - 206 2 : test_bidirectional_data_passing(); - 207 2 : test_multiple_suspend_points(); - 208 2 : test_conditional_suspension(); - 209 2 : test_null_data_passing(); - 210 2 : test_vmem_suspend_resume(); + 206 3 : test_bidirectional_data_passing(); + 207 3 : test_multiple_suspend_points(); + 208 3 : test_conditional_suspension(); + 209 3 : test_null_data_passing(); + 210 3 : test_vmem_suspend_resume(); 211 : - 212 2 : printf("\nAll tests passed!\n"); - 213 2 : return 0; + 212 3 : printf("\nAll tests passed!\n"); + 213 3 : return 0; 214 : } From 2816657736d46cf77d25624a6f9d64637bd1945b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 28 Apr 2026 20:55:56 +0000 Subject: [PATCH 07/14] Update code coverage badges [skip ci] --- docs/coverage_linux_aarch64.svg | 4 ++-- docs/coverage_linux_x86_64.svg | 4 ++-- docs/coverage_macos_aarch64.svg | 4 ++-- docs/coverage_macos_x86_64.svg | 2 +- docs/coverage_windows_x86_64.svg | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/coverage_linux_aarch64.svg b/docs/coverage_linux_aarch64.svg index 2997e32..452a425 100644 --- a/docs/coverage_linux_aarch64.svg +++ b/docs/coverage_linux_aarch64.svg @@ -8,13 +8,13 @@ - + coverage - 86.2% + 90.4% diff --git a/docs/coverage_linux_x86_64.svg b/docs/coverage_linux_x86_64.svg index 2997e32..452a425 100644 --- a/docs/coverage_linux_x86_64.svg +++ b/docs/coverage_linux_x86_64.svg @@ -8,13 +8,13 @@ - + coverage - 86.2% + 90.4% diff --git a/docs/coverage_macos_aarch64.svg b/docs/coverage_macos_aarch64.svg index f70ec20..235b19e 100644 --- a/docs/coverage_macos_aarch64.svg +++ b/docs/coverage_macos_aarch64.svg @@ -8,13 +8,13 @@ - + coverage - 95.3% + 89.3% diff --git a/docs/coverage_macos_x86_64.svg b/docs/coverage_macos_x86_64.svg index f70ec20..dd4cd15 100644 --- a/docs/coverage_macos_x86_64.svg +++ b/docs/coverage_macos_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 95.3% + 90.3% diff --git a/docs/coverage_windows_x86_64.svg b/docs/coverage_windows_x86_64.svg index 930a2b2..844817e 100644 --- a/docs/coverage_windows_x86_64.svg +++ b/docs/coverage_windows_x86_64.svg @@ -8,13 +8,13 @@ - + coverage - 89% + 95% From e226d8e4d3cf41d6e52e85563cc00948b4bcca49 Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Wed, 29 Apr 2026 12:35:59 -0700 Subject: [PATCH 08/14] Add volatile to stack buffer to make sure that stores are not eliminated by the compiler. Co-authored-by: Copilot --- tests/test_yafl_watermark.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/test_yafl_watermark.c b/tests/test_yafl_watermark.c index 85affb3..286da27 100644 --- a/tests/test_yafl_watermark.c +++ b/tests/test_yafl_watermark.c @@ -13,22 +13,31 @@ #include "../include/yafl.h" +#define TEST_STACK_TOUCH_COMPLETE 0x34 +#define TEST_STACK_TOUCH_SUSPEND_BEFORE 0xC9 +#define TEST_STACK_TOUCH_SUSPEND_AFTER 0x9C + static const char *alloc_name(yafl_stack_flags_t alloc_flag) { return alloc_flag == YAFL_STACK_FLAGS_VMEM ? "mmap" : "malloc"; } +/* Force observable stack writes so YAFL's own watermark logic has real usage to measure. */ +static void touch_volatile_stack_buffer(volatile unsigned char *buffer, size_t size, unsigned char value) { + for(size_t index = 0; index < size; index++) { buffer[index] = value; } +} + static void *complete_after_stack_use(void *arg) { volatile unsigned char stack_buffer[1536]; - memset((void *)stack_buffer, (int)(uintptr_t)arg, sizeof(stack_buffer)); + touch_volatile_stack_buffer(stack_buffer, sizeof(stack_buffer), TEST_STACK_TOUCH_COMPLETE); return arg; } static void *suspend_after_stack_use(void *arg) { volatile unsigned char stack_buffer[768]; - memset((void *)stack_buffer, 0x5A, sizeof(stack_buffer)); + touch_volatile_stack_buffer(stack_buffer, sizeof(stack_buffer), TEST_STACK_TOUCH_SUSPEND_BEFORE); arg = yafl_fiber_suspend(arg); - memset((void *)stack_buffer, 0xA5, sizeof(stack_buffer)); + touch_volatile_stack_buffer(stack_buffer, sizeof(stack_buffer), TEST_STACK_TOUCH_SUSPEND_AFTER); return arg; } From 94470143e165468666c5bbaf3a03246012a88cfb Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Wed, 29 Apr 2026 12:37:28 -0700 Subject: [PATCH 09/14] Remove fcontext names. --- include/yafl.h | 7 +++---- src/yafl.c | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/include/yafl.h b/include/yafl.h index 1ed2097..28195c6 100644 --- a/include/yafl.h +++ b/include/yafl.h @@ -24,8 +24,8 @@ extern "C" { * Configuration * ======================================================================== */ -#ifndef FCONTEXT_DEFAULT_STACK_SIZE - #define FCONTEXT_DEFAULT_STACK_SIZE (24 * 1024) +#ifndef YAFL_DEFAULT_STACK_SIZE + #define YAFL_DEFAULT_STACK_SIZE (24 * 1024) #endif /* ======================================================================== @@ -77,8 +77,7 @@ typedef void *(*yafl_fiber_fn)(void *arg); * - YAFL_STACK_FLAGS_WATERMARK is optional * - YAFL_STACK_FLAGS_NONE (no flags) is invalid */ -extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, - yafl_stack_flags_t flags); +extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, yafl_stack_flags_t flags); /* ======================================================================== diff --git a/src/yafl.c b/src/yafl.c index 7534080..04d5f88 100644 --- a/src/yafl.c +++ b/src/yafl.c @@ -45,11 +45,11 @@ extern void *yafl_switch(yafl_t *save, yafl_t target, void *data); * Constants and Types * ======================================================================== */ -#define FCONTEXT_FIBER_MAGIC 0xF1BE7001 -#define FCONTEXT_STACK_WATERMARK 0xA5 -#define FCONTEXT_STACK_ALIGNMENT 16 +#define YAFL_FIBER_MAGIC 0xF1BE7001 +#define YAFL_STACK_WATERMARK 0xA5 +#define YAFL_STACK_ALIGNMENT 16 -typedef enum { FCONTEXT_ALLOC_MALLOC, FCONTEXT_ALLOC_VMEM } yafl_alloc_type_t; +typedef enum { YAFL_ALLOC_MALLOC, YAFL_ALLOC_VMEM } yafl_alloc_type_t; /* Internal fiber structure */ struct yafl_fiber { @@ -84,9 +84,9 @@ static void fiber_entry_trampoline(void *arg); static void free_fiber_stack(yafl_fiber_t *fiber) { if(fiber == NULL || fiber->stack_region == NULL) { return; } - if(fiber->alloc_type == FCONTEXT_ALLOC_MALLOC) { + if(fiber->alloc_type == YAFL_ALLOC_MALLOC) { free(fiber->stack_region); - } else if(fiber->alloc_type == FCONTEXT_ALLOC_VMEM) { + } else if(fiber->alloc_type == YAFL_ALLOC_VMEM) { #ifdef _WIN32 VirtualFree(fiber->stack_region, 0, MEM_RELEASE); #else @@ -106,7 +106,7 @@ static bool initialize_fiber_context(yafl_fiber_t *fiber) { } static bool reinitialize_fiber_context_with_watermark(yafl_fiber_t *fiber) { - memset((char *)fiber->stack_top - fiber->stack_size, FCONTEXT_STACK_WATERMARK, fiber->stack_size); + memset((char *)fiber->stack_top - fiber->stack_size, YAFL_STACK_WATERMARK, fiber->stack_size); return initialize_fiber_context(fiber); } @@ -128,7 +128,7 @@ static size_t get_page_size(void) { static void *align_stack_pointer(void *ptr) { uintptr_t addr = (uintptr_t)ptr; - return (void *)(addr & ~((uintptr_t)FCONTEXT_STACK_ALIGNMENT - 1)); + return (void *)(addr & ~((uintptr_t)YAFL_STACK_ALIGNMENT - 1)); } /* ======================================================================== @@ -182,8 +182,8 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size if(fiber == NULL) { return NULL; } /* Initialize fiber structure */ - fiber->magic = FCONTEXT_FIBER_MAGIC; - fiber->alloc_type = use_vmem ? FCONTEXT_ALLOC_VMEM : FCONTEXT_ALLOC_MALLOC; + fiber->magic = YAFL_FIBER_MAGIC; + fiber->alloc_type = use_vmem ? YAFL_ALLOC_VMEM : YAFL_ALLOC_MALLOC; fiber->status = YAFL_FIBER_STATUS_SUSPENDED; fiber->user_entry = fiber_fn; fiber->cached_result = NULL; @@ -196,7 +196,7 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size fiber->stack_size = 0; /* Use default stack size if not specified */ - if(stack_size == 0) { stack_size = FCONTEXT_DEFAULT_STACK_SIZE; } + if(stack_size == 0) { stack_size = YAFL_DEFAULT_STACK_SIZE; } /* Allocate stack based on allocation type */ if(use_vmem) { @@ -271,7 +271,7 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { /* Validation */ - if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return NULL; } + if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC) { return NULL; } /* If complete, return cached result (idempotent) */ if(fiber->status == YAFL_FIBER_STATUS_COMPLETE) { return fiber->cached_result; } @@ -317,18 +317,18 @@ extern void *yafl_fiber_suspend(void *result) { * ======================================================================== */ extern yafl_fiber_status_t yafl_fiber_status(yafl_fiber_t *fiber) { - if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return YAFL_FIBER_STATUS_ERR; } + if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC) { return YAFL_FIBER_STATUS_ERR; } return fiber->status; } extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { - if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC || !fiber->watermark_filled) { return 0; } + if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC || !fiber->watermark_filled) { return 0; } /* Scan from stack base for watermark bytes */ unsigned char *stack_base = (unsigned char *)fiber->stack_top - fiber->stack_size; size_t unused = 0; - while(unused < fiber->stack_size && stack_base[unused] == FCONTEXT_STACK_WATERMARK) { unused++; } + while(unused < fiber->stack_size && stack_base[unused] == YAFL_STACK_WATERMARK) { unused++; } return fiber->stack_size - unused; } @@ -338,7 +338,7 @@ extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { * ======================================================================== */ extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { - if(fiber == NULL || fiber->magic != FCONTEXT_FIBER_MAGIC) { return; } + if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC) { return; } /* Cannot destroy running fiber */ if(fiber->status == YAFL_FIBER_STATUS_RUNNING) { return; } From 8033bbc4a0523d20a9d7c58f6a10a0c568dd1118 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 29 Apr 2026 19:43:02 +0000 Subject: [PATCH 10/14] Update code coverage badges [skip ci] --- docs/coverage_macos_x86_64.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/coverage_macos_x86_64.svg b/docs/coverage_macos_x86_64.svg index dd4cd15..452a425 100644 --- a/docs/coverage_macos_x86_64.svg +++ b/docs/coverage_macos_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 90.3% + 90.4% From 0edf9b6b32f2d8f25341dd2b58f61a136917e6e2 Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Wed, 29 Apr 2026 13:01:50 -0700 Subject: [PATCH 11/14] Cleaning up comments and calls to get the page size. Co-authored-by: Copilot --- src/yafl.c | 157 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 51 deletions(-) diff --git a/src/yafl.c b/src/yafl.c index 04d5f88..2350b37 100644 --- a/src/yafl.c +++ b/src/yafl.c @@ -26,9 +26,6 @@ #include "yafl.h" -/* ======================================================================== - * Low-Level API (Internal Only) - * ======================================================================== */ /* Raw context handle - opaque pointer to saved machine state */ typedef struct yafl_opaque_t *yafl_t; @@ -37,14 +34,28 @@ typedef struct yafl_opaque_t *yafl_t; typedef void (*yafl_entry_t)(void *); /* Low-level assembly-implemented functions */ -/* Creates the initial saved context for a fiber stack. */ + +/** + * @brief Create the initial saved context for a fiber stack. + * + * @param sp Top of the stack region. + * @param size Size of the usable stack region in bytes. + * @param fn Entry function that will run on the new context. + * @return Saved low-level context handle, or NULL on failure. + */ extern yafl_t yafl_make_context(void *sp, size_t size, yafl_entry_t fn); -extern void *yafl_switch(yafl_t *save, yafl_t target, void *data); -/* ======================================================================== - * Constants and Types - * ======================================================================== */ +/** + * @brief Switch execution from one context to another. + * + * @param save Receives the current context before switching away. + * @param target Context to resume. + * @param data User data passed across the switch. + * @return User data returned when control switches back. + */ +extern void *yafl_switch(yafl_t *save, yafl_t target, void *data); +/* Constants and types */ #define YAFL_FIBER_MAGIC 0xF1BE7001 #define YAFL_STACK_WATERMARK 0xA5 #define YAFL_STACK_ALIGNMENT 16 @@ -81,6 +92,11 @@ static _Thread_local yafl_fiber_t *tls_current_fiber = NULL; static void fiber_entry_trampoline(void *arg); +/** + * @brief Release any stack memory owned by a fiber. + * + * @param fiber Fiber whose stack allocation should be freed. + */ static void free_fiber_stack(yafl_fiber_t *fiber) { if(fiber == NULL || fiber->stack_region == NULL) { return; } @@ -100,44 +116,49 @@ static void free_fiber_stack(yafl_fiber_t *fiber) { fiber->stack_size = 0; } +/** + * @brief Create the initial low-level context for a fiber. + * + * @param fiber Fiber whose stack should be prepared for first entry. + * @return true if the context was created successfully, otherwise false. + */ static bool initialize_fiber_context(yafl_fiber_t *fiber) { fiber->context = yafl_make_context(fiber->stack_top, fiber->stack_size, fiber_entry_trampoline); return fiber->context != NULL; } + +/** + * @brief Fill a fiber stack with the watermark byte and rebuild its context. + * + * @param fiber Fiber whose stack should be watermarked. + * @return true if the context was rebuilt successfully, otherwise false. + */ static bool reinitialize_fiber_context_with_watermark(yafl_fiber_t *fiber) { memset((char *)fiber->stack_top - fiber->stack_size, YAFL_STACK_WATERMARK, fiber->stack_size); return initialize_fiber_context(fiber); } -/* ======================================================================== - * Utility Functions - * ======================================================================== */ -static size_t get_page_size(void) { -#ifdef _WIN32 - SYSTEM_INFO si; - GetSystemInfo(&si); - return (size_t)si.dwPageSize; -#else - long page_size = sysconf(_SC_PAGE_SIZE); - if(page_size <= 0) { return 4096; } - return (size_t)page_size; -#endif -} - -static void *align_stack_pointer(void *ptr) { +/** + * @brief Align a stack pointer down to the required stack alignment. + * + * @param ptr Unaligned stack pointer candidate. + * @return Aligned stack pointer. + */ +static inline void *align_stack_pointer(void *ptr) { uintptr_t addr = (uintptr_t)ptr; return (void *)(addr & ~((uintptr_t)YAFL_STACK_ALIGNMENT - 1)); } -/* ======================================================================== - * Trampoline: Adapts Low-Level API to High-Level Fiber API - * ======================================================================== */ -/* - * This is called as the entry function by yafl_make_context(). - * It wraps the user's entry function, manages state, and handles the result. +/** + * @brief Enter a fiber through the low-level context trampoline. + * + * Calls the user entry function, updates fiber state, caches the final + * result, and switches back to the resumer. + * + * @param arg Argument supplied by the first resume into the fiber. */ static void fiber_entry_trampoline(void *arg) { yafl_fiber_t *fiber = tls_current_fiber; @@ -162,10 +183,15 @@ static void fiber_entry_trampoline(void *arg) { abort(); } -/* ======================================================================== - * Fiber Creation - * ======================================================================== */ +/** + * @brief Create a fiber and allocate its backing stack. + * + * @param fiber_fn User entry function for the new fiber. + * @param stack_size Requested stack size in bytes, or 0 for the default. + * @param flags Stack allocation and feature flags. + * @return Newly created fiber, or NULL on failure. + */ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size, yafl_stack_flags_t flags) { /* Validate fiber function is not NULL */ if(fiber_fn == NULL) { return NULL; } @@ -200,7 +226,7 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size /* Allocate stack based on allocation type */ if(use_vmem) { - size_t page_size = get_page_size(); + size_t page_size = yafl_get_page_size(); size_t stack_with_overhead = stack_size + 256; size_t aligned_stack_size = ((stack_with_overhead + page_size - 1) / page_size) * page_size; size_t guard_size = page_size; @@ -265,10 +291,13 @@ extern yafl_fiber_t *yafl_fiber_create(yafl_fiber_fn fiber_fn, size_t stack_size return NULL; } -/* ======================================================================== - * Fiber Control Flow - * ======================================================================== */ - +/** + * @brief Resume a suspended fiber. + * + * @param fiber Fiber to resume. + * @param arg Argument delivered to the fiber. + * @return Value yielded or returned by the fiber, or NULL on error. + */ extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { /* Validation */ if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC) { return NULL; } @@ -292,6 +321,12 @@ extern void *yafl_fiber_resume(yafl_fiber_t *fiber, void *arg) { return result; } +/** + * @brief Suspend the current fiber and return control to its resumer. + * + * @param result Value yielded back to the resumer. + * @return Argument supplied by the next resume call, or NULL on error. + */ extern void *yafl_fiber_suspend(void *result) { yafl_fiber_t *current = tls_current_fiber; @@ -312,15 +347,23 @@ extern void *yafl_fiber_suspend(void *result) { return arg; } -/* ======================================================================== - * Fiber Status and Monitoring - * ======================================================================== */ - +/** + * @brief Query the status of a fiber. + * + * @param fiber Fiber to inspect. + * @return Current fiber status, or YAFL_FIBER_STATUS_ERR on invalid input. + */ extern yafl_fiber_status_t yafl_fiber_status(yafl_fiber_t *fiber) { if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC) { return YAFL_FIBER_STATUS_ERR; } return fiber->status; } +/** + * @brief Measure the maximum observed stack usage for a watermarked fiber. + * + * @param fiber Fiber to inspect. + * @return Number of bytes used, or 0 if watermarking is unavailable. + */ extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC || !fiber->watermark_filled) { return 0; } @@ -333,10 +376,11 @@ extern size_t yafl_fiber_stack_high_watermark(yafl_fiber_t *fiber) { return fiber->stack_size - unused; } -/* ======================================================================== - * Cleanup - * ======================================================================== */ - +/** + * @brief Destroy a fiber and release any owned resources. + * + * @param fiber Fiber to destroy. + */ extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { if(fiber == NULL || fiber->magic != YAFL_FIBER_MAGIC) { return; } @@ -350,8 +394,19 @@ extern void yafl_fiber_destroy(yafl_fiber_t *fiber) { free(fiber); } -/* ======================================================================== - * Utilities - * ======================================================================== */ - -extern size_t yafl_get_page_size(void) { return get_page_size(); } +/** + * @brief Return the host page size. + * + * @return System page size in bytes. + */ +extern size_t yafl_get_page_size(void) { +#ifdef _WIN32 + SYSTEM_INFO si; + GetSystemInfo(&si); + return (size_t)si.dwPageSize; +#else + long page_size = sysconf(_SC_PAGE_SIZE); + if(page_size <= 0) { return 4096; } + return (size_t)page_size; +#endif +} From dbfd50e1ff31526257a6410b1c8bb953680fafa3 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 29 Apr 2026 20:14:05 +0000 Subject: [PATCH 12/14] Update code coverage badges [skip ci] --- docs/coverage_linux_aarch64.svg | 2 +- docs/coverage_linux_x86_64.svg | 2 +- docs/coverage_macos_aarch64.svg | 2 +- docs/coverage_macos_x86_64.svg | 2 +- docs/coverage_windows_x86_64.svg | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/coverage_linux_aarch64.svg b/docs/coverage_linux_aarch64.svg index 452a425..156293b 100644 --- a/docs/coverage_linux_aarch64.svg +++ b/docs/coverage_linux_aarch64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 90.4% + 90.5% diff --git a/docs/coverage_linux_x86_64.svg b/docs/coverage_linux_x86_64.svg index 452a425..156293b 100644 --- a/docs/coverage_linux_x86_64.svg +++ b/docs/coverage_linux_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 90.4% + 90.5% diff --git a/docs/coverage_macos_aarch64.svg b/docs/coverage_macos_aarch64.svg index 235b19e..5371a6c 100644 --- a/docs/coverage_macos_aarch64.svg +++ b/docs/coverage_macos_aarch64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 89.3% + 89.4% diff --git a/docs/coverage_macos_x86_64.svg b/docs/coverage_macos_x86_64.svg index 452a425..156293b 100644 --- a/docs/coverage_macos_x86_64.svg +++ b/docs/coverage_macos_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 90.4% + 90.5% diff --git a/docs/coverage_windows_x86_64.svg b/docs/coverage_windows_x86_64.svg index 844817e..42ec6a0 100644 --- a/docs/coverage_windows_x86_64.svg +++ b/docs/coverage_windows_x86_64.svg @@ -15,6 +15,6 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> coverage - 95% + 96% From fe6dcd32b387a22fcdda62f26524bb6e6abad243 Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Wed, 29 Apr 2026 13:33:58 -0700 Subject: [PATCH 13/14] Update/fix the i386 and mips64 assembly. Looks like there was an error changing away from the old API. --- src/asm/i386/switch_i386_sysv_elf_gas.S | 2 +- src/asm/mips64/switch_mips64_n64_elf_gas.S | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/asm/i386/switch_i386_sysv_elf_gas.S b/src/asm/i386/switch_i386_sysv_elf_gas.S index e00444c..28a412e 100644 --- a/src/asm/i386/switch_i386_sysv_elf_gas.S +++ b/src/asm/i386/switch_i386_sysv_elf_gas.S @@ -77,7 +77,7 @@ yafl_switch: movl 0x14(%esp), %ebx /* restore EBX */ movl 0x18(%esp), %ebp /* restore EBP */ - leal 0x24(%esp), %esp /* prepare stack */ + leal 0x20(%esp), %esp /* prepare stack for direct data return */ /* return and pass data directly */ movl %edx, %eax diff --git a/src/asm/mips64/switch_mips64_n64_elf_gas.S b/src/asm/mips64/switch_mips64_n64_elf_gas.S index 13a62ef..d8dd367 100644 --- a/src/asm/mips64/switch_mips64_n64_elf_gas.S +++ b/src/asm/mips64/switch_mips64_n64_elf_gas.S @@ -79,11 +79,11 @@ yafl_switch: s.d $f31, 56($sp) # save F31 #endif - # store SP (pointing to old context-data) in v0 as return - move $v0, $sp + # save current context through the first argument pointer + sd $sp, 0($a0) - # get SP (pointing to new context-data) from a0 param - move $sp, $a0 + # restore target context from the second argument + move $sp, $a1 #if defined(__mips_hard_float) l.d $f24, 0($sp) # restore F24 @@ -113,8 +113,9 @@ yafl_switch: # adjust stack daddiu $sp, $sp, 160 - move $a0, $v0 # move old sp from v0 to a0 as param - move $v1, $a1 # move *data from a1 to v1 as return + # return and pass data directly + move $v0, $a2 + move $a0, $a2 # jump to context jr $t9 From d7a54b0078b4b06ffa73a97713f64edb5e3da002 Mon Sep 17 00:00:00 2001 From: Kyle Hayes Date: Wed, 29 Apr 2026 13:55:33 -0700 Subject: [PATCH 14/14] Miscalculation on stack reservations. Maybe fixed? --- src/asm/i386/make_context_i386_sysv_elf_gas.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asm/i386/make_context_i386_sysv_elf_gas.S b/src/asm/i386/make_context_i386_sysv_elf_gas.S index e30b990..430cf93 100644 --- a/src/asm/i386/make_context_i386_sysv_elf_gas.S +++ b/src/asm/i386/make_context_i386_sysv_elf_gas.S @@ -42,7 +42,7 @@ yafl_make_context: andl $-16, %eax /* reserve space for context-data on context-stack, and align the stack */ - leal -0x34(%eax), %eax + leal -0x30(%eax), %eax /* third arg of yafl_make_context() == address of context-function */ /* stored in EBX */