diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-13 13:07:53 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-13 13:07:53 -0200 |
commit | fdc25a1ebfe9968dcec390dd556375105aa0be40 (patch) | |
tree | 43759131636a501ec92475d453fd1a1c73bc8090 | |
parent | 3b06f983ae0e57b90cdeb500c84bb524e5c3635b (diff) | |
download | lua-fdc25a1ebfe9968dcec390dd556375105aa0be40.tar.gz lua-fdc25a1ebfe9968dcec390dd556375105aa0be40.tar.bz2 lua-fdc25a1ebfe9968dcec390dd556375105aa0be40.zip |
New functions 'lua_resetthread' and 'coroutine.kill'
New functions to reset/kill a thread/coroutine, mainly (only?) to
close any pending to-be-closed variable. ('lua_resetthread' also
allows a thread to be reused...)
-rw-r--r-- | lcorolib.c | 58 | ||||
-rw-r--r-- | ldo.c | 4 | ||||
-rw-r--r-- | lfunc.c | 18 | ||||
-rw-r--r-- | lfunc.h | 11 | ||||
-rw-r--r-- | lstate.c | 27 | ||||
-rw-r--r-- | ltests.c | 3 | ||||
-rw-r--r-- | lua.h | 1 | ||||
-rw-r--r-- | lvm.c | 2 | ||||
-rw-r--r-- | manual/manual.of | 34 | ||||
-rw-r--r-- | testes/coroutine.lua | 45 | ||||
-rw-r--r-- | testes/main.lua | 24 |
11 files changed, 195 insertions, 32 deletions
@@ -107,29 +107,40 @@ static int luaB_yield (lua_State *L) { | |||
107 | } | 107 | } |
108 | 108 | ||
109 | 109 | ||
110 | static int luaB_costatus (lua_State *L) { | 110 | #define COS_RUN 0 |
111 | lua_State *co = getco(L); | 111 | #define COS_DEAD 1 |
112 | if (L == co) lua_pushliteral(L, "running"); | 112 | #define COS_YIELD 2 |
113 | #define COS_NORM 3 | ||
114 | |||
115 | |||
116 | static const char *statname[] = {"running", "dead", "suspended", "normal"}; | ||
117 | |||
118 | |||
119 | static int auxstatus (lua_State *L, lua_State *co) { | ||
120 | if (L == co) return COS_RUN; | ||
113 | else { | 121 | else { |
114 | switch (lua_status(co)) { | 122 | switch (lua_status(co)) { |
115 | case LUA_YIELD: | 123 | case LUA_YIELD: |
116 | lua_pushliteral(L, "suspended"); | 124 | return COS_YIELD; |
117 | break; | ||
118 | case LUA_OK: { | 125 | case LUA_OK: { |
119 | lua_Debug ar; | 126 | lua_Debug ar; |
120 | if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ | 127 | if (lua_getstack(co, 0, &ar)) /* does it have frames? */ |
121 | lua_pushliteral(L, "normal"); /* it is running */ | 128 | return COS_NORM; /* it is running */ |
122 | else if (lua_gettop(co) == 0) | 129 | else if (lua_gettop(co) == 0) |
123 | lua_pushliteral(L, "dead"); | 130 | return COS_DEAD; |
124 | else | 131 | else |
125 | lua_pushliteral(L, "suspended"); /* initial state */ | 132 | return COS_YIELD; /* initial state */ |
126 | break; | ||
127 | } | 133 | } |
128 | default: /* some error occurred */ | 134 | default: /* some error occurred */ |
129 | lua_pushliteral(L, "dead"); | 135 | return COS_DEAD; |
130 | break; | ||
131 | } | 136 | } |
132 | } | 137 | } |
138 | } | ||
139 | |||
140 | |||
141 | static int luaB_costatus (lua_State *L) { | ||
142 | lua_State *co = getco(L); | ||
143 | lua_pushstring(L, statname[auxstatus(L, co)]); | ||
133 | return 1; | 144 | return 1; |
134 | } | 145 | } |
135 | 146 | ||
@@ -147,6 +158,28 @@ static int luaB_corunning (lua_State *L) { | |||
147 | } | 158 | } |
148 | 159 | ||
149 | 160 | ||
161 | static int luaB_kill (lua_State *L) { | ||
162 | lua_State *co = getco(L); | ||
163 | int status = auxstatus(L, co); | ||
164 | switch (status) { | ||
165 | case COS_DEAD: case COS_YIELD: { | ||
166 | status = lua_resetthread(co); | ||
167 | if (status == LUA_OK) { | ||
168 | lua_pushboolean(L, 1); | ||
169 | return 1; | ||
170 | } | ||
171 | else { | ||
172 | lua_pushboolean(L, 0); | ||
173 | lua_xmove(co, L, 1); /* copy error message */ | ||
174 | return 2; | ||
175 | } | ||
176 | } | ||
177 | default: /* normal or running coroutine */ | ||
178 | return luaL_error(L, "cannot kill a %s coroutine", statname[status]); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | |||
150 | static const luaL_Reg co_funcs[] = { | 183 | static const luaL_Reg co_funcs[] = { |
151 | {"create", luaB_cocreate}, | 184 | {"create", luaB_cocreate}, |
152 | {"resume", luaB_coresume}, | 185 | {"resume", luaB_coresume}, |
@@ -155,6 +188,7 @@ static const luaL_Reg co_funcs[] = { | |||
155 | {"wrap", luaB_cowrap}, | 188 | {"wrap", luaB_cowrap}, |
156 | {"yield", luaB_yield}, | 189 | {"yield", luaB_yield}, |
157 | {"isyieldable", luaB_yieldable}, | 190 | {"isyieldable", luaB_yieldable}, |
191 | {"kill", luaB_kill}, | ||
158 | {NULL, NULL} | 192 | {NULL, NULL} |
159 | }; | 193 | }; |
160 | 194 | ||
@@ -98,6 +98,10 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { | |||
98 | setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); | 98 | setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); |
99 | break; | 99 | break; |
100 | } | 100 | } |
101 | case CLOSEPROTECT: { | ||
102 | setnilvalue(s2v(oldtop)); /* no error message */ | ||
103 | break; | ||
104 | } | ||
101 | default: { | 105 | default: { |
102 | setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ | 106 | setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ |
103 | break; | 107 | break; |
@@ -127,17 +127,18 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { | |||
127 | 127 | ||
128 | 128 | ||
129 | /* | 129 | /* |
130 | ** Prepare and call a closing method. If status is OK, code is | 130 | ** Prepare and call a closing method. If status is OK, code is still |
131 | ** still inside the original protected call, and so any error | 131 | ** inside the original protected call, and so any error will be handled |
132 | ** will be handled there. Otherwise, a previous error already | 132 | ** there. Otherwise, a previous error already activated original |
133 | ** activated original protected call, and so the call to the | 133 | ** protected call, and so the call to the closing method must be |
134 | ** closing method must be protected here. | 134 | ** protected here. (A status = CLOSEPROTECT behaves like a previous |
135 | ** error, to also run the closing method in protected mode). | ||
135 | ** If status is OK, the call to the closing method will be pushed | 136 | ** If status is OK, the call to the closing method will be pushed |
136 | ** at the top of the stack. Otherwise, values are pushed after | 137 | ** at the top of the stack. Otherwise, values are pushed after |
137 | ** the 'level' of the upvalue being closed, as everything after | 138 | ** the 'level' of the upvalue being closed, as everything after |
138 | ** that won't be used again. | 139 | ** that won't be used again. |
139 | */ | 140 | */ |
140 | static int closeupval (lua_State *L, TValue *uv, StkId level, int status) { | 141 | static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) { |
141 | if (likely(status == LUA_OK)) { | 142 | if (likely(status == LUA_OK)) { |
142 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ | 143 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ |
143 | callclose(L, NULL); /* call closing method */ | 144 | callclose(L, NULL); /* call closing method */ |
@@ -207,9 +208,10 @@ int luaF_close (lua_State *L, StkId level, int status) { | |||
207 | if (!iswhite(uv)) | 208 | if (!iswhite(uv)) |
208 | gray2black(uv); /* closed upvalues cannot be gray */ | 209 | gray2black(uv); /* closed upvalues cannot be gray */ |
209 | luaC_barrier(L, uv, slot); | 210 | luaC_barrier(L, uv, slot); |
210 | if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */ | 211 | if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) { |
212 | /* must run closing method */ | ||
211 | ptrdiff_t levelrel = savestack(L, level); | 213 | ptrdiff_t levelrel = savestack(L, level); |
212 | status = closeupval(L, uv->v, upl, status); /* may realloc. the stack */ | 214 | status = callclosemth(L, uv->v, upl, status); /* may change the stack */ |
213 | level = restorestack(L, levelrel); | 215 | level = restorestack(L, levelrel); |
214 | } | 216 | } |
215 | } | 217 | } |
@@ -42,6 +42,17 @@ | |||
42 | #define MAXMISS 10 | 42 | #define MAXMISS 10 |
43 | 43 | ||
44 | 44 | ||
45 | /* | ||
46 | ** Special "status" for 'luaF_close' | ||
47 | */ | ||
48 | |||
49 | /* close upvalues without running their closing methods */ | ||
50 | #define NOCLOSINGMETH (-1) | ||
51 | |||
52 | /* close upvalues running all closing methods in protected mode */ | ||
53 | #define CLOSEPROTECT (-2) | ||
54 | |||
55 | |||
45 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); | 56 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); |
46 | LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); | 57 | LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); |
47 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); | 58 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); |
@@ -258,7 +258,7 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
258 | 258 | ||
259 | static void close_state (lua_State *L) { | 259 | static void close_state (lua_State *L) { |
260 | global_State *g = G(L); | 260 | global_State *g = G(L); |
261 | luaF_close(L, L->stack, -1); /* close all upvalues for this thread */ | 261 | luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */ |
262 | luaC_freeallobjects(L); /* collect all objects */ | 262 | luaC_freeallobjects(L); /* collect all objects */ |
263 | if (ttisnil(&g->nilvalue)) /* closing a fully built state? */ | 263 | if (ttisnil(&g->nilvalue)) /* closing a fully built state? */ |
264 | luai_userstateclose(L); | 264 | luai_userstateclose(L); |
@@ -301,7 +301,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { | |||
301 | 301 | ||
302 | void luaE_freethread (lua_State *L, lua_State *L1) { | 302 | void luaE_freethread (lua_State *L, lua_State *L1) { |
303 | LX *l = fromstate(L1); | 303 | LX *l = fromstate(L1); |
304 | luaF_close(L1, L1->stack, -1); /* close all upvalues for this thread */ | 304 | luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */ |
305 | lua_assert(L1->openupval == NULL); | 305 | lua_assert(L1->openupval == NULL); |
306 | luai_userstatefree(L, L1); | 306 | luai_userstatefree(L, L1); |
307 | freestack(L1); | 307 | freestack(L1); |
@@ -309,6 +309,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) { | |||
309 | } | 309 | } |
310 | 310 | ||
311 | 311 | ||
312 | int lua_resetthread (lua_State *L) { | ||
313 | CallInfo *ci; | ||
314 | int status; | ||
315 | lua_lock(L); | ||
316 | ci = &L->base_ci; | ||
317 | status = luaF_close(L, L->stack, CLOSEPROTECT); | ||
318 | setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */ | ||
319 | if (status != CLOSEPROTECT) /* real errors? */ | ||
320 | luaD_seterrorobj(L, status, L->stack + 1); | ||
321 | else { | ||
322 | status = LUA_OK; | ||
323 | L->top = L->stack + 1; | ||
324 | } | ||
325 | ci->callstatus = CIST_C; | ||
326 | ci->func = L->stack; | ||
327 | ci->top = L->top + LUA_MINSTACK; | ||
328 | L->ci = ci; | ||
329 | L->status = status; | ||
330 | lua_unlock(L); | ||
331 | return status; | ||
332 | } | ||
333 | |||
334 | |||
312 | LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | 335 | LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { |
313 | int i; | 336 | int i; |
314 | lua_State *L; | 337 | lua_State *L; |
@@ -1366,6 +1366,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { | |||
1366 | else if EQ("newthread") { | 1366 | else if EQ("newthread") { |
1367 | lua_newthread(L1); | 1367 | lua_newthread(L1); |
1368 | } | 1368 | } |
1369 | else if EQ("resetthread") { | ||
1370 | lua_pushinteger(L1, lua_resetthread(L1)); | ||
1371 | } | ||
1369 | else if EQ("newuserdata") { | 1372 | else if EQ("newuserdata") { |
1370 | lua_newuserdata(L1, getnum); | 1373 | lua_newuserdata(L1, getnum); |
1371 | } | 1374 | } |
@@ -147,6 +147,7 @@ extern const char lua_ident[]; | |||
147 | LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); | 147 | LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); |
148 | LUA_API void (lua_close) (lua_State *L); | 148 | LUA_API void (lua_close) (lua_State *L); |
149 | LUA_API lua_State *(lua_newthread) (lua_State *L); | 149 | LUA_API lua_State *(lua_newthread) (lua_State *L); |
150 | LUA_API int (lua_resetthread) (lua_State *L); | ||
150 | 151 | ||
151 | LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); | 152 | LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); |
152 | 153 | ||
@@ -1565,7 +1565,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1565 | if (nparams1) /* vararg function? */ | 1565 | if (nparams1) /* vararg function? */ |
1566 | delta = ci->u.l.nextraargs + nparams1; | 1566 | delta = ci->u.l.nextraargs + nparams1; |
1567 | /* close upvalues from current call */ | 1567 | /* close upvalues from current call */ |
1568 | luaF_close(L, base, -1); /* (no to-be-closed vars. here) */ | 1568 | luaF_close(L, base, LUA_OK); |
1569 | updatestack(ci); | 1569 | updatestack(ci); |
1570 | } | 1570 | } |
1571 | if (!ttisfunction(s2v(ra))) { /* not a function? */ | 1571 | if (!ttisfunction(s2v(ra))) { /* not a function? */ |
diff --git a/manual/manual.of b/manual/manual.of index 0e8e3d72..862d032b 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -3927,6 +3927,19 @@ and then pops the top element. | |||
3927 | 3927 | ||
3928 | } | 3928 | } |
3929 | 3929 | ||
3930 | @APIEntry{int lua_resetthread (lua_State *L);| | ||
3931 | @apii{0,?,-} | ||
3932 | |||
3933 | Resets a thread, cleaning its call stack and closing all pending | ||
3934 | to-be-closed variables. | ||
3935 | Returns a status code: | ||
3936 | @Lid{LUA_OK} for no errors in closing methods, | ||
3937 | or an error status otherwise. | ||
3938 | In case of error, | ||
3939 | leave the error object on the stack, | ||
3940 | |||
3941 | } | ||
3942 | |||
3930 | @APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs, | 3943 | @APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs, |
3931 | int *nresults);| | 3944 | int *nresults);| |
3932 | @apii{?,?,-} | 3945 | @apii{?,?,-} |
@@ -3948,11 +3961,8 @@ or returned by the body function. | |||
3948 | @Lid{LUA_OK} if the coroutine finishes its execution | 3961 | @Lid{LUA_OK} if the coroutine finishes its execution |
3949 | without errors, | 3962 | without errors, |
3950 | or an error code in case of errors @seeC{lua_pcall}. | 3963 | or an error code in case of errors @seeC{lua_pcall}. |
3951 | |||
3952 | In case of errors, | 3964 | In case of errors, |
3953 | the stack is not unwound, | 3965 | the error object is on the top of the stack. |
3954 | so you can use the debug API over it. | ||
3955 | The error object is on the top of the stack. | ||
3956 | 3966 | ||
3957 | To resume a coroutine, | 3967 | To resume a coroutine, |
3958 | you remove all results from the last @Lid{lua_yield}, | 3968 | you remove all results from the last @Lid{lua_yield}, |
@@ -6285,6 +6295,17 @@ it is not inside a non-yieldable @N{C function}. | |||
6285 | 6295 | ||
6286 | } | 6296 | } |
6287 | 6297 | ||
6298 | @LibEntry{coroutine.kill(co)| | ||
6299 | |||
6300 | Kills coroutine @id{co}, | ||
6301 | closing all its pending to-be-closed variables | ||
6302 | and putting the coroutine in a dead state. | ||
6303 | In case of error closing some variable, | ||
6304 | returns @false plus the error object; | ||
6305 | otherwise returns @true. | ||
6306 | |||
6307 | } | ||
6308 | |||
6288 | @LibEntry{coroutine.resume (co [, val1, @Cdots])| | 6309 | @LibEntry{coroutine.resume (co [, val1, @Cdots])| |
6289 | 6310 | ||
6290 | Starts or continues the execution of coroutine @id{co}. | 6311 | Starts or continues the execution of coroutine @id{co}. |
@@ -8648,6 +8669,11 @@ has been removed. | |||
8648 | When needed, this metamethod must be explicitly defined. | 8669 | When needed, this metamethod must be explicitly defined. |
8649 | } | 8670 | } |
8650 | 8671 | ||
8672 | @item{ | ||
8673 | When a coroutine finishes with an error, | ||
8674 | its stack is unwound (to run any pending closing methods). | ||
8675 | } | ||
8676 | |||
8651 | } | 8677 | } |
8652 | 8678 | ||
8653 | } | 8679 | } |
diff --git a/testes/coroutine.lua b/testes/coroutine.lua index 7d42eadd..5674a4dd 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua | |||
@@ -119,6 +119,51 @@ end | |||
119 | assert(#a == 25 and a[#a] == 97) | 119 | assert(#a == 25 and a[#a] == 97) |
120 | x, a = nil | 120 | x, a = nil |
121 | 121 | ||
122 | |||
123 | -- coroutine kill | ||
124 | do | ||
125 | -- ok to kill a dead coroutine | ||
126 | local co = coroutine.create(print) | ||
127 | assert(coroutine.resume(co, "testing 'coroutine.kill'")) | ||
128 | assert(coroutine.status(co) == "dead") | ||
129 | assert(coroutine.kill(co)) | ||
130 | |||
131 | -- cannot kill the running coroutine | ||
132 | local st, msg = pcall(coroutine.kill, coroutine.running()) | ||
133 | assert(not st and string.find(msg, "running")) | ||
134 | |||
135 | local main = coroutine.running() | ||
136 | |||
137 | -- cannot kill a "normal" coroutine | ||
138 | ;(coroutine.wrap(function () | ||
139 | local st, msg = pcall(coroutine.kill, main) | ||
140 | assert(not st and string.find(msg, "normal")) | ||
141 | end))() | ||
142 | |||
143 | -- to-be-closed variables in coroutines | ||
144 | local X | ||
145 | co = coroutine.create(function () | ||
146 | local *toclose x = function (err) assert(err == nil); X = false end | ||
147 | X = true | ||
148 | coroutine.yield() | ||
149 | end) | ||
150 | coroutine.resume(co) | ||
151 | assert(X) | ||
152 | assert(coroutine.kill(co)) | ||
153 | assert(not X and coroutine.status(co) == "dead") | ||
154 | |||
155 | -- error killing a coroutine | ||
156 | co = coroutine.create(function() | ||
157 | local *toclose x = function (err) assert(err == nil); error(111) end | ||
158 | coroutine.yield() | ||
159 | end) | ||
160 | coroutine.resume(co) | ||
161 | local st, msg = coroutine.kill(co) | ||
162 | assert(not st and coroutine.status(co) == "dead" and msg == 111) | ||
163 | |||
164 | end | ||
165 | |||
166 | |||
122 | -- yielding across C boundaries | 167 | -- yielding across C boundaries |
123 | 168 | ||
124 | co = coroutine.wrap(function() | 169 | co = coroutine.wrap(function() |
diff --git a/testes/main.lua b/testes/main.lua index c7bde0d9..b9dcab1c 100644 --- a/testes/main.lua +++ b/testes/main.lua | |||
@@ -254,15 +254,15 @@ NoRun("error object is a table value", [[lua %s]], prog) | |||
254 | 254 | ||
255 | 255 | ||
256 | -- chunk broken in many lines | 256 | -- chunk broken in many lines |
257 | s = [=[ -- | 257 | s = [=[ -- |
258 | function f ( x ) | 258 | function f ( x ) |
259 | local a = [[ | 259 | local a = [[ |
260 | xuxu | 260 | xuxu |
261 | ]] | 261 | ]] |
262 | local b = "\ | 262 | local b = "\ |
263 | xuxu\n" | 263 | xuxu\n" |
264 | if x == 11 then return 1 + 12 , 2 + 20 end --[[ test multiple returns ]] | 264 | if x == 11 then return 1 + 12 , 2 + 20 end --[[ test multiple returns ]] |
265 | return x + 1 | 265 | return x + 1 |
266 | --\\ | 266 | --\\ |
267 | end | 267 | end |
268 | return( f( 100 ) ) | 268 | return( f( 100 ) ) |
@@ -272,10 +272,10 @@ s = string.gsub(s, ' ', '\n\n') -- change all spaces for newlines | |||
272 | prepfile(s) | 272 | prepfile(s) |
273 | RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) | 273 | RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) |
274 | checkprogout("101\n13\t22\n\n") | 274 | checkprogout("101\n13\t22\n\n") |
275 | 275 | ||
276 | prepfile[[#comment in 1st line without \n at the end]] | 276 | prepfile[[#comment in 1st line without \n at the end]] |
277 | RUN('lua %s', prog) | 277 | RUN('lua %s', prog) |
278 | 278 | ||
279 | prepfile[[#test line number when file starts with comment line | 279 | prepfile[[#test line number when file starts with comment line |
280 | debug = require"debug" | 280 | debug = require"debug" |
281 | print(debug.getinfo(1).currentline) | 281 | print(debug.getinfo(1).currentline) |
@@ -306,6 +306,20 @@ NoRun("", "lua %s", prog) -- no message | |||
306 | prepfile("os.exit(false, true)") | 306 | prepfile("os.exit(false, true)") |
307 | NoRun("", "lua %s", prog) -- no message | 307 | NoRun("", "lua %s", prog) -- no message |
308 | 308 | ||
309 | |||
310 | -- to-be-closed variables in main chunk | ||
311 | prepfile[[ | ||
312 | local *toclose x = function (err) | ||
313 | assert(err == 120) | ||
314 | print("Ok") | ||
315 | end | ||
316 | local *toclose e1 = function () error(120) end | ||
317 | os.exit(true, true) | ||
318 | ]] | ||
319 | RUN('lua %s > %s', prog, out) | ||
320 | checkprogout("Ok") | ||
321 | |||
322 | |||
309 | -- remove temporary files | 323 | -- remove temporary files |
310 | assert(os.remove(prog)) | 324 | assert(os.remove(prog)) |
311 | assert(os.remove(otherprog)) | 325 | assert(os.remove(otherprog)) |