| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | // |
| 5 | // Platform-specific context switching using ucontext (Linux, macOS, BSDs). |
| 6 | // This provides the low-level mechanism for goroutine context switches, |
| 7 | // analogous to Go's gogo/gosave assembly routines. |
| 8 | module goroutines |
| 9 | |
| 10 | #flag -D_XOPEN_SOURCE=700 |
| 11 | #flag darwin -D_DARWIN_C_SOURCE |
| 12 | #include "@VMODROOT/vlib/goroutines/context_nix.h" |
| 13 | |
| 14 | fn C.goroutines_context_alloc() voidptr |
| 15 | fn C.goroutines_context_init(ctx voidptr, stack voidptr, stack_size int, entry_fn voidptr, arg voidptr) |
| 16 | fn C.goroutines_context_switch(from voidptr, to voidptr) |
| 17 | fn C.goroutines_context_set(to voidptr) |
| 18 | |
| 19 | // Context wraps ucontext_t for goroutine context switching. |
| 20 | // The actual ucontext_t is heap-allocated in C to avoid issues with |
| 21 | // ucontext_t being a typedef rather than a struct tag. |
| 22 | pub struct Context { |
| 23 | pub mut: |
| 24 | uctx voidptr // heap-allocated ucontext_t |
| 25 | } |
| 26 | |
| 27 | // context_init initializes a context for a new goroutine. |
| 28 | // Sets up the context to run `entry_fn` with `arg` on the given stack. |
| 29 | pub fn context_init(mut ctx Context, stack voidptr, stack_size int, entry_fn fn (voidptr), arg voidptr) { |
| 30 | if ctx.uctx == unsafe { nil } { |
| 31 | ctx.uctx = C.goroutines_context_alloc() |
| 32 | } |
| 33 | C.goroutines_context_init(ctx.uctx, stack, stack_size, voidptr(entry_fn), arg) |
| 34 | } |
| 35 | |
| 36 | // context_switch saves the current context into `from` and switches to `to`. |
| 37 | pub fn context_switch(mut from Context, to &Context) { |
| 38 | if from.uctx == unsafe { nil } { |
| 39 | from.uctx = C.goroutines_context_alloc() |
| 40 | } |
| 41 | C.goroutines_context_switch(from.uctx, to.uctx) |
| 42 | } |
| 43 | |
| 44 | // context_set switches to the given context without saving. |
| 45 | pub fn context_set(to &Context) { |
| 46 | C.goroutines_context_set(to.uctx) |
| 47 | } |
| 48 | |