forked from yuin/gopher-lua
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcoroutinelib.go
More file actions
165 lines (150 loc) · 3.59 KB
/
coroutinelib.go
File metadata and controls
165 lines (150 loc) · 3.59 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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package lua
func OpenCoroutine(L *LState) int {
// TODO: Tie module name to contents of linit.go?
mod := L.RegisterModule(CoroutineLibName, coFuncs)
L.Push(mod)
return 1
}
var coFuncs = map[string]LGFunction{
"create": coCreate,
"isyieldable": coIsYieldable,
"resume": coResume,
"running": coRunning,
"status": coStatus,
"wrap": coWrap,
"yield": coYield,
}
func coCreate(L *LState) int {
fn := L.CheckFunction(1)
newthread, _ := L.NewThread()
base := 0
newthread.stack.Push(callFrame{
Fn: fn,
Pc: 0,
Base: base,
LocalBase: base + 1,
ReturnBase: base,
NArgs: 0,
NRet: MultRet,
Parent: nil,
TailCall: 0,
})
L.Push(newthread)
return 1
}
// coroutine.isyieldable([thread])
// Returns true if the given thread can yield.
// In Lua 5.3+, a thread can yield if it's not the main thread and not dead.
func coIsYieldable(L *LState) int {
var th *LState
if L.GetTop() == 0 {
// No argument: check current thread
th = L
} else {
th = L.CheckThread(1)
}
// The main thread cannot yield
if th == L.G.MainThread {
L.Push(LFalse)
return 1
}
// Dead threads cannot yield
if th.Dead {
L.Push(LFalse)
return 1
}
// A suspended/resumed thread can always yield
// (unlike the running main thread)
L.Push(LTrue)
return 1
}
func coYield(L *LState) int {
// Check if we're in the main thread or not in a coroutine context
// In Lua 5.3+, yield is allowed if:
// 1. We're not in the main thread (L.Parent != nil), OR
// 2. The current thread is a wrapped coroutine (L.wrapped == true), OR
// 3. The current running thread (G.CurrentThread) is different from main thread
inCoroutine := L.Parent != nil || L.wrapped || (L.G.CurrentThread != nil && L.G.CurrentThread != L.G.MainThread)
if !inCoroutine {
L.raiseError(1, "can not yield from outside of a coroutine")
return 0
}
// Check if we're trying to yield across a C boundary
// In Lua 5.3+, yield is allowed inside pcall/xpcall for Lua functions
// But yield is still not allowed directly inside C functions (like table.sort)
// We allow yield from C functions if we're in a coroutine context (Lua 5.3 behavior)
// Only disallow yield across metamethod boundary (nCcalls > 1)
if L.nCcalls > 1 {
L.raiseError(1, "attempt to yield across metamethod/c-call boundary")
return 0
}
return -1
}
func coResume(L *LState) int {
th := L.CheckThread(1)
if L.G.CurrentThread == th {
msg := "can not resume a running thread"
if th.wrapped {
L.RaiseError(msg)
return 0
}
L.Push(LFalse)
L.Push(LString(msg))
return 2
}
if th.Dead {
msg := "can not resume a dead thread"
if th.wrapped {
L.RaiseError(msg)
return 0
}
L.Push(LFalse)
L.Push(LString(msg))
return 2
}
th.Parent = L
L.G.CurrentThread = th
if !th.isStarted() {
cf := th.stack.Last()
th.currentFrame = cf
th.SetTop(0)
nargs := L.GetTop() - 1
L.XMoveTo(th, nargs)
cf.NArgs = nargs
th.initCallFrame(cf)
th.Panic = panicWithoutTraceback
} else {
nargs := L.GetTop() - 1
L.XMoveTo(th, nargs)
}
top := L.GetTop()
threadRun(th)
return L.GetTop() - top
}
func coRunning(L *LState) int {
if L.G.MainThread == L {
L.Push(L.G.MainThread)
L.Push(LTrue)
return 2
}
L.Push(L.G.CurrentThread)
L.Push(LFalse)
return 2
}
func coStatus(L *LState) int {
L.Push(LString(L.Status(L.CheckThread(1))))
return 1
}
func wrapaux(L *LState) int {
L.Insert(L.ToThread(UpvalueIndex(1)), 1)
return coResume(L)
}
func coWrap(L *LState) int {
coCreate(L)
L.CheckThread(L.GetTop()).wrapped = true
v := L.Get(L.GetTop())
L.Pop(1)
L.Push(L.NewClosure(wrapaux, v))
return 1
}
//