Skip to content

Commit f62ae61

Browse files
committed
bugfix: coroutine.wrap: propagate errors to the parent coroutine.
1 parent 9887569 commit f62ae61

File tree

5 files changed

+367
-30
lines changed

5 files changed

+367
-30
lines changed

src/ngx_http_lua_common.h

+7
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,13 @@ struct ngx_http_lua_co_ctx_s {
431431
the ngx.thread.spawn()
432432
call */
433433
unsigned sem_resume_status:1;
434+
435+
unsigned is_wrap; /* set when creating coroutines via
436+
coroutine.wrap */
437+
438+
unsigned propagate_error; /* set when propagating an error
439+
from a coroutine to its
440+
parent */
434441
};
435442

436443

src/ngx_http_lua_coroutine.c

+51-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626

2727
static int ngx_http_lua_coroutine_create(lua_State *L);
28+
static int ngx_http_lua_coroutine_wrap(lua_State *L);
2829
static int ngx_http_lua_coroutine_resume(lua_State *L);
2930
static int ngx_http_lua_coroutine_yield(lua_State *L);
3031
static int ngx_http_lua_coroutine_status(lua_State *L);
@@ -62,6 +63,45 @@ ngx_http_lua_coroutine_create(lua_State *L)
6263
}
6364

6465

66+
static int
67+
ngx_http_lua_coroutine_wrap_runner(lua_State *L)
68+
{
69+
/* retrieve closure and insert it at the bottom of
70+
* the stack for coroutine.resume() */
71+
lua_pushvalue(L, lua_upvalueindex(1));
72+
lua_insert(L, 1);
73+
74+
return ngx_http_lua_coroutine_resume(L);
75+
}
76+
77+
78+
static int
79+
ngx_http_lua_coroutine_wrap(lua_State *L)
80+
{
81+
ngx_http_request_t *r;
82+
ngx_http_lua_ctx_t *ctx;
83+
ngx_http_lua_co_ctx_t *coctx = NULL;
84+
85+
r = ngx_http_lua_get_req(L);
86+
if (r == NULL) {
87+
return luaL_error(L, "no request found");
88+
}
89+
90+
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
91+
if (ctx == NULL) {
92+
return luaL_error(L, "no request ctx found");
93+
}
94+
95+
ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx);
96+
97+
coctx->is_wrap = 1;
98+
99+
lua_pushcclosure(L, ngx_http_lua_coroutine_wrap_runner, 1);
100+
101+
return 1;
102+
}
103+
104+
65105
int
66106
ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r,
67107
ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx)
@@ -246,7 +286,7 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
246286
int rc;
247287

248288
/* new coroutine table */
249-
lua_createtable(L, 0 /* narr */, 14 /* nrec */);
289+
lua_createtable(L, 0 /* narr */, 16 /* nrec */);
250290

251291
/* get old coroutine table */
252292
lua_getglobal(L, "coroutine");
@@ -258,6 +298,9 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
258298
lua_getfield(L, -1, "create");
259299
lua_setfield(L, -3, "_create");
260300

301+
lua_getfield(L, -1, "wrap");
302+
lua_setfield(L, -3, "_wrap");
303+
261304
lua_getfield(L, -1, "resume");
262305
lua_setfield(L, -3, "_resume");
263306

@@ -273,6 +316,9 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
273316
lua_pushcfunction(L, ngx_http_lua_coroutine_create);
274317
lua_setfield(L, -2, "__create");
275318

319+
lua_pushcfunction(L, ngx_http_lua_coroutine_wrap);
320+
lua_setfield(L, -2, "__wrap");
321+
276322
lua_pushcfunction(L, ngx_http_lua_coroutine_resume);
277323
lua_setfield(L, -2, "__resume");
278324

@@ -287,7 +333,7 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
287333
/* inject coroutine APIs */
288334
{
289335
const char buf[] =
290-
"local keys = {'create', 'yield', 'resume', 'status'}\n"
336+
"local keys = {'create', 'yield', 'resume', 'status', 'wrap'}\n"
291337
"local getfenv = getfenv\n"
292338
"for _, key in ipairs(keys) do\n"
293339
"local std = coroutine['_' .. key]\n"
@@ -305,24 +351,19 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
305351
"return std(...)\n"
306352
"end\n"
307353
"end\n"
308-
"local create, resume = coroutine.create, coroutine.resume\n"
309-
"coroutine.wrap = function(f)\n"
310-
"local co = create(f)\n"
311-
"return function(...) return select(2, resume(co, ...)) end\n"
312-
"end\n"
313354
"package.loaded.coroutine = coroutine";
314355

315356
#if 0
316357
"debug.sethook(function () collectgarbage() end, 'rl', 1)"
317358
#endif
318359
;
319360

320-
rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine.wrap");
361+
rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine_api");
321362
}
322363

323364
if (rc != 0) {
324365
ngx_log_error(NGX_LOG_ERR, log, 0,
325-
"failed to load Lua code for coroutine.wrap(): %i: %s",
366+
"failed to load Lua code for coroutine_api: %i: %s",
326367
rc, lua_tostring(L, -1));
327368

328369
lua_pop(L, 1);
@@ -332,7 +373,7 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
332373
rc = lua_pcall(L, 0, 0, 0);
333374
if (rc != 0) {
334375
ngx_log_error(NGX_LOG_ERR, log, 0,
335-
"failed to run the Lua code for coroutine.wrap(): %i: %s",
376+
"failed to run the Lua code for coroutine_api: %i: %s",
336377
rc, lua_tostring(L, -1));
337378
lua_pop(L, 1);
338379
}

src/ngx_http_lua_util.c

+43-18
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
950950
int rv, success = 1;
951951
lua_State *next_co;
952952
lua_State *old_co;
953-
const char *err, *msg, *trace;
953+
const char *err = NULL, *msg = NULL, *trace = NULL;
954954
ngx_int_t rc;
955955
#if (NGX_PCRE)
956956
ngx_pool_t *old_pool = NULL;
@@ -988,6 +988,8 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
988988
dd("ctx: %p", ctx);
989989
dd("cur co: %p", ctx->cur_co_ctx->co);
990990
dd("cur co status: %d", ctx->cur_co_ctx->co_status);
991+
dd("cur co has propagated error: %d",
992+
ctx->cur_co_ctx->propagate_error);
991993

992994
orig_coctx = ctx->cur_co_ctx;
993995

@@ -1004,6 +1006,11 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
10041006
}
10051007
#endif
10061008

1009+
if (ctx->cur_co_ctx->propagate_error) {
1010+
ctx->cur_co_ctx->propagate_error = 0;
1011+
goto propagate_error;
1012+
}
1013+
10071014
ngx_http_lua_assert(orig_coctx->co_top + nrets
10081015
== lua_gettop(orig_coctx->co));
10091016

@@ -1148,12 +1155,6 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
11481155
next_coctx = ctx->cur_co_ctx->parent_co_ctx;
11491156
next_co = next_coctx->co;
11501157

1151-
/*
1152-
* prepare return values for coroutine.resume
1153-
* (true plus any retvals)
1154-
*/
1155-
lua_pushboolean(next_co, 1);
1156-
11571158
if (nrets) {
11581159
dd("moving %d return values to next co", nrets);
11591160
lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
@@ -1162,7 +1163,15 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
11621163
#endif
11631164
}
11641165

1165-
nrets++; /* add the true boolean value */
1166+
if (!ctx->cur_co_ctx->is_wrap) {
1167+
/*
1168+
* prepare return values for coroutine.resume
1169+
* (true plus any retvals)
1170+
*/
1171+
lua_pushboolean(next_co, 1);
1172+
lua_insert(next_co, 1);
1173+
nrets++; /* add the true boolean value */
1174+
}
11661175

11671176
ctx->cur_co_ctx = next_coctx;
11681177

@@ -1273,12 +1282,6 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
12731282

12741283
next_co = next_coctx->co;
12751284

1276-
/*
1277-
* ended successful, coroutine.resume returns true plus
1278-
* any return values
1279-
*/
1280-
lua_pushboolean(next_co, success);
1281-
12821285
if (nrets) {
12831286
lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
12841287
}
@@ -1288,7 +1291,16 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
12881291
ctx->uthreads--;
12891292
}
12901293

1291-
nrets++;
1294+
if (!ctx->cur_co_ctx->is_wrap) {
1295+
/*
1296+
* ended successful, coroutine.resume returns true plus
1297+
* any return values
1298+
*/
1299+
lua_pushboolean(next_co, success);
1300+
lua_insert(next_co, 1);
1301+
nrets++;
1302+
}
1303+
12921304
ctx->cur_co_ctx = next_coctx;
12931305

12941306
ngx_http_lua_probe_info("set parent running");
@@ -1344,6 +1356,10 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
13441356
ctx->cur_co_ctx);
13451357
trace = lua_tostring(L, -1);
13461358

1359+
propagate_error:
1360+
1361+
ngx_http_lua_assert(err != NULL && msg != NULL && trace != NULL);
1362+
13471363
if (ctx->cur_co_ctx->is_uthread) {
13481364

13491365
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -1433,16 +1449,25 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
14331449

14341450
next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
14351451

1452+
ctx->cur_co_ctx = next_coctx;
1453+
1454+
if (orig_coctx->is_wrap) {
1455+
/*
1456+
* coroutine.wrap propagates errors
1457+
* to the parent
1458+
*/
1459+
next_coctx->propagate_error = 1;
1460+
continue;
1461+
}
1462+
14361463
/*
14371464
* ended with error, coroutine.resume returns false plus
14381465
* err msg
14391466
*/
14401467
lua_pushboolean(next_co, 0);
1441-
lua_xmove(ctx->cur_co_ctx->co, next_co, 1);
1468+
lua_xmove(orig_coctx->co, next_co, 1);
14421469
nrets = 2;
14431470

1444-
ctx->cur_co_ctx = next_coctx;
1445-
14461471
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
14471472
"lua coroutine: %s: %s\n%s", err, msg, trace);
14481473

t/062-count.t

+1-1
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ probe process("$LIBLUA_PATH").function("rehashtab") {
391391
--- stap_out2
392392
3
393393
--- response_body
394-
coroutine: 14
394+
coroutine: 16
395395
--- no_error_log
396396
[error]
397397

0 commit comments

Comments
 (0)