Skip to content

Commit aa18430

Browse files
committed
Changing the API required so that we do not have to call jump_fcontext() at the end of the coroutine function.
1 parent e5fb443 commit aa18430

11 files changed

Lines changed: 139 additions & 104 deletions

include/fcontext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ typedef struct {
8888
* Called when context is first entered via jump_fcontext.
8989
* The transfer_t contains the previous context and initial data.
9090
*/
91-
typedef void (*fcontext_fn_t)(fcontext_transfer_t);
91+
typedef fcontext_transfer_t (*fcontext_fn_t)(fcontext_transfer_t);
9292

9393
/**
9494
* Function run "on top" of an existing context.

src/fcontext_wrapper.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,35 @@ static size_t get_page_size(void) {
4444
#endif
4545
}
4646

47+
/* Trampoline function that wraps the user's entry point */
48+
static fcontext_transfer_t context_trampoline(fcontext_transfer_t t) {
49+
/* 1. Retrieve the user function passed from fcontext_init_trampoline */
50+
fcontext_fn_t fn = (fcontext_fn_t)t.data;
51+
52+
/* 2. Yield back to fcontext_create immediately.
53+
* We return control to the creator so it can return the context to the user.
54+
*/
55+
t = jump_fcontext(t.prev_context, NULL);
56+
57+
/* 3. We are now resumed by the user (via fcontext_swap/jump_fcontext).
58+
* t.prev_context is the user's context.
59+
* t.data is the user's data.
60+
*/
61+
t = fn(t);
62+
63+
/* 4. Automatic cleanup: Jump back to the last known context */
64+
return jump_fcontext(t.prev_context, t.data);
65+
}
66+
67+
/* Helper to initialize the trampoline. Call this inside fcontext_create* functions. */
68+
static fcontext_t fcontext_init_trampoline(fcontext_t ctx, fcontext_fn_t fn) {
69+
/* Perform a context switch to the new context to pass the function pointer.
70+
* The trampoline will read 'fn' from 'data' and immediately yield back.
71+
*/
72+
fcontext_transfer_t t = jump_fcontext(ctx, (void *)fn);
73+
return t.prev_context;
74+
}
75+
4776
/**
4877
* Create a new context using malloc with software guard zones.
4978
*/
@@ -92,7 +121,9 @@ fcontext_stack_t *fcontext_create_malloc(size_t stack_size, size_t guard_size, f
92121
/* The stack pointer passed to make_fcontext must be aligned */
93122
sp = fcontext_align_stack_pointer(sp);
94123

95-
ctx->context = make_fcontext(sp, stack_size, entry_fn);
124+
/* Create context pointing to trampoline, then init it */
125+
ctx->context = make_fcontext(sp, stack_size, (fcontext_fn_t)context_trampoline);
126+
ctx->context = fcontext_init_trampoline(ctx->context, entry_fn);
96127
ctx->alloc_type = FCONTEXT_ALLOC_MALLOC;
97128
ctx->alloc_base = raw_alloc; /* Store original malloc pointer for free() */
98129
ctx->stack_base = stack_base;
@@ -180,7 +211,9 @@ fcontext_stack_t *fcontext_create_mmap(size_t stack_size, size_t guard_size, fco
180211
#endif
181212

182213
void *sp = (char *)stack_base + stack_size;
183-
ctx->context = make_fcontext(sp, stack_size, entry_fn);
214+
/* Create context pointing to trampoline, then init it */
215+
ctx->context = make_fcontext(sp, stack_size, (fcontext_fn_t)context_trampoline);
216+
ctx->context = fcontext_init_trampoline(ctx->context, entry_fn);
184217
ctx->alloc_type = FCONTEXT_ALLOC_MMAP;
185218
ctx->alloc_base = region;
186219
ctx->stack_base = stack_base;

tests/test_fcontext_alignment.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
* Distributed under the Boost Software License, Version 1.0.
1111
*/
1212

13-
#include <stdio.h>
14-
#include <stdlib.h>
13+
#include "fcontext.h"
1514
#include <assert.h>
1615
#include <stdint.h>
17-
#include "fcontext.h"
16+
#include <stdio.h>
17+
#include <stdlib.h>
1818

1919
static int alignment_ok = 0;
2020

21-
void align_fiber(fcontext_transfer_t t) {
21+
fcontext_transfer_t align_fiber(fcontext_transfer_t t) {
2222
uintptr_t sp = 0;
2323

2424
/* Capture Stack Pointer using inline assembly */
@@ -60,29 +60,29 @@ void align_fiber(fcontext_transfer_t t) {
6060
* Call pushes 8-byte return address.
6161
* On entry: RSP % 16 == 8.
6262
*/
63-
if ((sp & 0xF) == 0x8) {
63+
if((sp & 0xF) == 0x8) {
6464
printf(" [fiber] x86_64 ABI verified (RSP %% 16 == 8)\n");
6565
alignment_ok = 1;
66-
} else if ((sp & 0xF) == 0x0) {
66+
} else if((sp & 0xF) == 0x0) {
6767
/* Some runtimes/compilers might force 16-byte alignment on entry for SIMD optimization */
6868
printf(" [fiber] 16-byte alignment verified (RSP %% 16 == 0)\n");
6969
alignment_ok = 1;
7070
}
7171
#elif defined(__aarch64__)
7272
/* AAPCS64: SP must be 16-byte aligned at all times */
73-
if ((sp & 0xF) == 0x0) {
73+
if((sp & 0xF) == 0x0) {
7474
printf(" [fiber] ARM64 AAPCS verified (SP %% 16 == 0)\n");
7575
alignment_ok = 1;
7676
}
7777
#else
7878
/* Default check: 8-byte or 16-byte alignment is generally acceptable for others */
79-
if ((sp & 0x7) == 0x0) {
79+
if((sp & 0x7) == 0x0) {
8080
printf(" [fiber] Basic alignment verified (>= 8 bytes)\n");
8181
alignment_ok = 1;
8282
}
8383
#endif
8484

85-
jump_fcontext(t.prev_context, NULL);
85+
return t;
8686
}
8787

8888
int main(void) {
@@ -95,7 +95,7 @@ int main(void) {
9595

9696
fcontext_destroy(state);
9797

98-
if (alignment_ok) {
98+
if(alignment_ok) {
9999
printf("\n✓ PASS: Stack alignment requirements met\n");
100100
return 0;
101101
} else {

tests/test_fcontext_basic.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@
1111
* http://www.boost.org/LICENSE_1_0.txt)
1212
*/
1313

14-
#include <stdio.h>
15-
#include <stdlib.h>
14+
#include "fcontext.h"
1615
#include <assert.h>
1716
#include <stdint.h>
18-
#include "fcontext.h"
17+
#include <stdio.h>
18+
#include <stdlib.h>
1919

2020
static int test_phase = 0;
2121

2222
/**
2323
* Fiber function - entry point for new context
2424
*/
25-
void fiber_func(fcontext_transfer_t t) {
25+
fcontext_transfer_t fiber_func(fcontext_transfer_t t) {
2626
printf(" [fiber] entered context\n");
2727
fflush(stdout);
2828
assert(test_phase == 0);
@@ -39,6 +39,9 @@ void fiber_func(fcontext_transfer_t t) {
3939

4040
printf(" [fiber] finishing\n");
4141
fflush(stdout);
42+
43+
/* Return to trampoline to switch back */
44+
return t;
4245
}
4346

4447
int main(void) {
@@ -47,7 +50,7 @@ int main(void) {
4750

4851
/* Create context with guarded stack using mmap */
4952
fcontext_stack_t *state = fcontext_create(24 * 1024, fiber_func);
50-
if (state == NULL) {
53+
if(state == NULL) {
5154
fprintf(stderr, "Failed to create context\n");
5255
return 1;
5356
}

tests/test_fcontext_guard.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static void *g_stack_base = NULL;
3737
static size_t g_stack_size = 0;
3838

3939
/* Recursive function to consume stack until overflow */
40-
void overflow_fiber(fcontext_transfer_t t) {
40+
fcontext_transfer_t overflow_fiber(fcontext_transfer_t t) {
4141
/* Allocate 1KB on stack */
4242
/* Force alignment to avoid potential SIGILL from SIMD instructions on unaligned stack */
4343
#if defined(_MSC_VER)
@@ -58,6 +58,7 @@ void overflow_fiber(fcontext_transfer_t t) {
5858

5959
/* Prevent tail call optimization */
6060
(void)buffer[0];
61+
return t;
6162
}
6263

6364
#ifndef _WIN32

tests/test_fcontext_low_level.c

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,58 +9,58 @@
99
* Distributed under the Boost Software License, Version 1.0.
1010
*/
1111

12+
#include "fcontext.h"
13+
#include <assert.h>
1214
#include <stdio.h>
1315
#include <stdlib.h>
14-
#include <assert.h>
15-
#include "fcontext.h"
1616

1717
static int fiber_ran = 0;
1818

19-
void low_level_fiber(fcontext_transfer_t t) {
19+
fcontext_transfer_t low_level_fiber(fcontext_transfer_t t) {
2020
printf(" [fiber] entered low-level fiber\n");
21-
21+
2222
/* Verify data passed */
23-
int* value = (int*)t.data;
23+
int *value = (int *)t.data;
2424
assert(*value == 123);
25-
25+
2626
fiber_ran = 1;
27-
27+
2828
printf(" [fiber] jumping back\n");
29-
jump_fcontext(t.prev_context, NULL);
29+
return jump_fcontext(t.prev_context, NULL);
3030
}
3131

3232
int main(void) {
3333
printf("=== fcontext Low-Level API Test (make_fcontext) ===\n");
34-
34+
3535
/* 1. Manually allocate stack */
3636
size_t stack_size = 16 * 1024;
37-
void* stack_buffer = malloc(stack_size);
38-
if (!stack_buffer) {
37+
void *stack_buffer = malloc(stack_size);
38+
if(!stack_buffer) {
3939
fprintf(stderr, "Failed to allocate stack\n");
4040
return 1;
4141
}
42-
42+
4343
/* 2. Calculate aligned stack pointer (top of stack) */
44-
void* sp = (char*)stack_buffer + stack_size;
44+
void *sp = (char *)stack_buffer + stack_size;
4545
sp = fcontext_align_stack_pointer(sp);
46-
46+
4747
printf("[main] Stack allocated at %p, SP aligned to %p\n", stack_buffer, sp);
48-
48+
4949
/* 3. Create context directly */
5050
fcontext_t ctx = make_fcontext(sp, stack_size, low_level_fiber);
5151
assert(ctx != NULL);
52-
52+
5353
/* 4. Jump to context */
5454
int data = 123;
5555
printf("[main] Jumping to fiber...\n");
5656
jump_fcontext(ctx, &data);
57-
57+
5858
printf("[main] Returned from fiber\n");
5959
assert(fiber_ran == 1);
60-
60+
6161
/* 5. Cleanup */
6262
free(stack_buffer);
63-
63+
6464
printf("\n✓ PASS: Low-level make_fcontext test completed.\n");
6565
return 0;
6666
}

tests/test_fcontext_ontop.c

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
* Distributed under the Boost Software License, Version 1.0.
77
*/
88

9+
#include "fcontext.h"
10+
#include <assert.h>
911
#include <stdio.h>
1012
#include <stdlib.h>
11-
#include <assert.h>
12-
#include "fcontext.h"
1313

1414
typedef struct {
1515
int value;
@@ -18,63 +18,63 @@ typedef struct {
1818
/* Function to be executed "on top" of the fiber */
1919
fcontext_transfer_t ontop_func(fcontext_transfer_t t) {
2020
printf(" [ontop] Executing ontop function\n");
21-
test_data_t* data = (test_data_t*)t.data;
22-
21+
test_data_t *data = (test_data_t *)t.data;
22+
2323
/* Verify we received the expected data from main */
2424
assert(data->value == 3);
25-
25+
2626
/* Modify data to prove we ran */
2727
data->value += 10;
28-
28+
2929
/* Return transfer to resume the fiber */
3030
return t;
3131
}
3232

33-
void fiber_func(fcontext_transfer_t t) {
33+
fcontext_transfer_t fiber_func(fcontext_transfer_t t) {
3434
printf(" [fiber] Started\n");
35-
test_data_t* data = (test_data_t*)t.data;
36-
35+
test_data_t *data = (test_data_t *)t.data;
36+
3737
/* 1. Verify initial value passed from main */
3838
assert(data->value == 1);
39-
39+
4040
/* Modify and yield back */
4141
data->value = 2;
4242
printf(" [fiber] Yielding back to main\n");
43-
43+
4444
/* Use fcontext_swap for standard yield */
4545
t = fcontext_swap(t.prev_context, data);
46-
46+
4747
/* 2. We are back. ontop_fcontext should have executed ontop_func before we resumed. */
4848
printf(" [fiber] Resumed\n");
49-
data = (test_data_t*)t.data;
50-
49+
data = (test_data_t *)t.data;
50+
5151
/* Verify ontop_func modification (3 + 10 = 13) */
5252
assert(data->value == 13);
53-
53+
5454
printf(" [fiber] Finished\n");
55-
fcontext_swap(t.prev_context, data);
55+
return fcontext_swap(t.prev_context, data);
5656
}
5757

5858
int main(void) {
5959
printf("=== fcontext ontop Test ===\n");
60-
61-
fcontext_stack_t* stack = fcontext_create(16 * 1024, fiber_func);
62-
if (!stack) {
60+
61+
fcontext_stack_t *stack = fcontext_create(16 * 1024, fiber_func);
62+
if(!stack) {
6363
fprintf(stderr, "Failed to create context\n");
6464
return 1;
6565
}
66-
67-
test_data_t data = { .value = 1 };
68-
66+
67+
test_data_t data = {.value = 1};
68+
6969
printf("[main] Jumping to fiber\n");
7070
fcontext_transfer_t t = fcontext_swap(stack->context, &data);
71-
71+
7272
/* Fiber yielded. Value should be 2. */
7373
assert(data.value == 2);
74-
74+
7575
printf("[main] Fiber yielded. Calling ontop_fcontext\n");
7676
data.value = 3;
77-
77+
7878
/* Use ontop_fcontext to resume fiber and run ontop_func first */
7979
/* Note: We manually handle ASAN hooks here since ontop_fcontext is raw API */
8080
#ifdef __SANITIZE_ADDRESS__
@@ -85,11 +85,11 @@ int main(void) {
8585
#ifdef __SANITIZE_ADDRESS__
8686
__sanitizer_finish_switch_fiber(fake_stack_save, NULL, NULL);
8787
#endif
88-
88+
8989
printf("[main] Fiber returned\n");
90-
90+
9191
fcontext_destroy(stack);
92-
92+
9393
printf("\n✓ PASS: ontop_fcontext test completed.\n");
9494
return 0;
9595
}

0 commit comments

Comments
 (0)