Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/VM/src/lcorolib.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
3
#include "lualib.h"
4
5
#include "ldebug.h"
6
#include "lstate.h"
7
#include "lvm.h"
8
9
#define CO_STATUS_ERROR -1
10
#define CO_STATUS_BREAK -2
11
12
static const char* const statnames[] = {"running", "suspended", "normal", "dead", "dead"}; // dead appears twice for LUA_COERR and LUA_COFIN
13
14
static int costatus(lua_State* L)
15
{
16
lua_State* co = lua_tothread(L, 1);
17
luaL_argexpected(L, co, 1, "thread");
18
lua_pushstring(L, statnames[lua_costatus(L, co)]);
19
return 1;
20
}
21
22
static int auxresume(lua_State* L, lua_State* co, int narg)
23
{
24
// error handling for edge cases
25
if (co->status != LUA_YIELD)
26
{
27
int status = lua_costatus(L, co);
28
if (status != LUA_COSUS)
29
{
30
lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
31
return CO_STATUS_ERROR;
32
}
33
}
34
35
if (narg)
36
{
37
if (!lua_checkstack(co, narg))
38
luaL_error(L, "too many arguments to resume");
39
lua_xmove(L, co, narg);
40
}
41
else
42
{
43
// coroutine might be completely full already
44
if ((co->top - co->base) > LUAI_MAXCSTACK)
45
luaL_error(L, "too many arguments to resume");
46
}
47
48
co->singlestep = L->singlestep;
49
50
int status = lua_resume(co, L, narg);
51
if (status == 0 || status == LUA_YIELD)
52
{
53
int nres = cast_int(co->top - co->base);
54
if (nres)
55
{
56
// +1 accounts for true/false status in resumefinish
57
if (nres + 1 > LUA_MINSTACK && !lua_checkstack(L, nres + 1))
58
luaL_error(L, "too many results to resume");
59
lua_xmove(co, L, nres); // move yielded values
60
}
61
return nres;
62
}
63
else if (status == LUA_BREAK)
64
{
65
return CO_STATUS_BREAK;
66
}
67
else
68
{
69
lua_xmove(co, L, 1); // move error message
70
return CO_STATUS_ERROR;
71
}
72
}
73
74
static int interruptThread(lua_State* L, lua_State* co)
75
{
76
// notify the debugger that the thread was suspended
77
if (L->global->cb.debuginterrupt)
78
luau_callhook(L, L->global->cb.debuginterrupt, co);
79
80
return lua_break(L);
81
}
82
83
static int auxresumecont(lua_State* L, lua_State* co)
84
{
85
if (co->status == 0 || co->status == LUA_YIELD)
86
{
87
int nres = cast_int(co->top - co->base);
88
if (!lua_checkstack(L, nres + 1))
89
luaL_error(L, "too many results to resume");
90
lua_xmove(co, L, nres); // move yielded values
91
return nres;
92
}
93
else
94
{
95
lua_rawcheckstack(L, 2);
96
lua_xmove(co, L, 1); // move error message
97
return CO_STATUS_ERROR;
98
}
99
}
100
101
static int coresumefinish(lua_State* L, int r)
102
{
103
if (r < 0)
104
{
105
lua_pushboolean(L, 0);
106
lua_insert(L, -2);
107
return 2; // return false + error message
108
}
109
else
110
{
111
lua_pushboolean(L, 1);
112
lua_insert(L, -(r + 1));
113
return r + 1; // return true + `resume' returns
114
}
115
}
116
117
static int coresumey(lua_State* L)
118
{
119
lua_State* co = lua_tothread(L, 1);
120
luaL_argexpected(L, co, 1, "thread");
121
int narg = cast_int(L->top - L->base) - 1;
122
int r = auxresume(L, co, narg);
123
124
if (r == CO_STATUS_BREAK)
125
return interruptThread(L, co);
126
127
return coresumefinish(L, r);
128
}
129
130
static int coresumecont(lua_State* L, int status)
131
{
132
lua_State* co = lua_tothread(L, 1);
133
luaL_argexpected(L, co, 1, "thread");
134
135
// if coroutine still hasn't yielded after the break, break current thread again
136
if (co->status == LUA_BREAK)
137
return interruptThread(L, co);
138
139
int r = auxresumecont(L, co);
140
141
return coresumefinish(L, r);
142
}
143
144
static int auxwrapfinish(lua_State* L, int r)
145
{
146
if (r < 0)
147
{
148
if (lua_isstring(L, -1))
149
{ // error object is a string?
150
luaL_where(L, 1); // add extra info
151
lua_insert(L, -2);
152
lua_concat(L, 2);
153
}
154
lua_error(L); // propagate error
155
}
156
return r;
157
}
158
159
static int auxwrapy(lua_State* L)
160
{
161
lua_State* co = lua_tothread(L, lua_upvalueindex(1));
162
int narg = cast_int(L->top - L->base);
163
int r = auxresume(L, co, narg);
164
165
if (r == CO_STATUS_BREAK)
166
return interruptThread(L, co);
167
168
return auxwrapfinish(L, r);
169
}
170
171
static int auxwrapcont(lua_State* L, int status)
172
{
173
lua_State* co = lua_tothread(L, lua_upvalueindex(1));
174
175
// if coroutine still hasn't yielded after the break, break current thread again
176
if (co->status == LUA_BREAK)
177
return interruptThread(L, co);
178
179
int r = auxresumecont(L, co);
180
181
return auxwrapfinish(L, r);
182
}
183
184
static int cocreate(lua_State* L)
185
{
186
luaL_checktype(L, 1, LUA_TFUNCTION);
187
lua_State* NL = lua_newthread(L);
188
lua_xpush(L, NL, 1); // push function on top of NL
189
return 1;
190
}
191
192
static int cowrap(lua_State* L)
193
{
194
cocreate(L);
195
196
lua_pushcclosurek(L, auxwrapy, NULL, 1, auxwrapcont);
197
return 1;
198
}
199
200
static int coyield(lua_State* L)
201
{
202
int nres = cast_int(L->top - L->base);
203
return lua_yield(L, nres);
204
}
205
206
static int corunning(lua_State* L)
207
{
208
if (lua_pushthread(L))
209
lua_pushnil(L); // main thread is not a coroutine
210
return 1;
211
}
212
213
static int coyieldable(lua_State* L)
214
{
215
lua_pushboolean(L, lua_isyieldable(L));
216
return 1;
217
}
218
219
static int coclose(lua_State* L)
220
{
221
lua_State* co = lua_tothread(L, 1);
222
luaL_argexpected(L, co, 1, "thread");
223
224
int status = lua_costatus(L, co);
225
if (status != LUA_COFIN && status != LUA_COERR && status != LUA_COSUS)
226
luaL_error(L, "cannot close %s coroutine", statnames[status]);
227
228
if (co->status == LUA_OK || co->status == LUA_YIELD)
229
{
230
lua_pushboolean(L, true);
231
lua_resetthread(co);
232
return 1;
233
}
234
else
235
{
236
lua_pushboolean(L, false);
237
238
if (co->status == LUA_ERRMEM)
239
lua_pushstring(L, LUA_MEMERRMSG);
240
else if (co->status == LUA_ERRERR)
241
lua_pushstring(L, LUA_ERRERRMSG);
242
else if (lua_gettop(co))
243
lua_xmove(co, L, 1); // move error message
244
245
lua_resetthread(co);
246
return 2;
247
}
248
}
249
250
static const luaL_Reg co_funcs[] = {
251
{"create", cocreate},
252
{"running", corunning},
253
{"status", costatus},
254
{"wrap", cowrap},
255
{"yield", coyield},
256
{"isyieldable", coyieldable},
257
{"close", coclose},
258
{NULL, NULL},
259
};
260
261
int luaopen_coroutine(lua_State* L)
262
{
263
luaL_register(L, LUA_COLIBNAME, co_funcs);
264
265
lua_pushcclosurek(L, coresumey, "resume", 0, coresumecont);
266
lua_setfield(L, -2, "resume");
267
268
return 1;
269
}
270
271