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