1 //go:build scheduler.tasks || scheduler.cores
2 3 package task
4 5 import (
6 "unsafe"
7 )
8 9 //go:linkname runtimePanic runtime.runtimePanic
10 func runtimePanic(str string)
11 12 // Stack canary, to detect a stack overflow. The number is a random number
13 // generated by random.org. The bit fiddling dance is necessary because
14 // otherwise Go wouldn't allow the cast to a smaller integer size.
15 const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))
16 17 // state is a structure which holds a reference to the state of the task.
18 // When the task is suspended, the registers are stored onto the stack and the stack pointer is stored into sp.
19 type state struct {
20 // sp is the stack pointer of the saved state.
21 // When the task is inactive, the saved registers are stored at the top of the stack.
22 // Note: this should ideally be a unsafe.Pointer for the precise GC. The GC
23 // will find the stack through canaryPtr though so it's not currently a
24 // problem to store this value as uintptr.
25 sp uintptr
26 27 // canaryPtr points to the top word of the stack (the lowest address).
28 // This is used to detect stack overflows.
29 // When initializing the goroutine, the stackCanary constant is stored there.
30 // If the stack overflowed, the word will likely no longer equal stackCanary.
31 canaryPtr *uintptr
32 }
33 34 //export moxie_task_exit
35 func taskExit() {
36 // TODO: explicitly free the stack after switching back to the scheduler.
37 Pause()
38 }
39 40 // initialize the state and prepare to call the specified function with the specified argument bundle.
41 func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
42 // Create a stack.
43 stack := runtime_alloc(stackSize, nil)
44 45 // Set up the stack canary, a random number that should be checked when
46 // switching from the task back to the scheduler. The stack canary pointer
47 // points to the first word of the stack. If it has changed between now and
48 // the next stack switch, there was a stack overflow.
49 s.canaryPtr = (*uintptr)(stack)
50 *s.canaryPtr = stackCanary
51 52 // Get a pointer to the top of the stack, where the initial register values
53 // are stored. They will be popped off the stack on the first stack switch
54 // to the goroutine, and will start running moxie_startTask (this setup
55 // happens in archInit).
56 r := (*calleeSavedRegs)(unsafe.Add(stack, stackSize-unsafe.Sizeof(calleeSavedRegs{})))
57 58 // Invoke architecture-specific initialization.
59 s.archInit(r, fn, args)
60 }
61 62 //export moxie_swapTask
63 func swapTask(oldStack uintptr, newStack *uintptr)
64 65 // startTask is a small wrapper function that sets up the first (and only)
66 // argument to the new goroutine and makes sure it is exited when the goroutine
67 // finishes.
68 //
69 //go:extern moxie_startTask
70 var startTask [0]uint8
71 72 // start creates and starts a new goroutine with the given function and arguments.
73 // The new goroutine is scheduled to run later.
74 func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
75 t := &Task{}
76 t.state.initialize(fn, args, stackSize)
77 scheduleTask(t)
78 }
79 80 // OnSystemStack returns whether the caller is running on the system stack.
81 func OnSystemStack() bool {
82 // If there is not an active goroutine, then this must be running on the system stack.
83 return Current() == nil
84 }
85