Skip to content

Commit 9a892b8

Browse files
committed
bugfix: coroutine.wrap: propagate errors to the parent coroutine.
1 parent c658e22 commit 9a892b8

File tree

5 files changed

+503
-41
lines changed

5 files changed

+503
-41
lines changed

src/ngx_http_lua_common.h

+7
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,13 @@ struct ngx_http_lua_co_ctx_s {
482482
the ngx.thread.spawn()
483483
call */
484484
unsigned sem_resume_status:1;
485+
486+
unsigned is_wrap:1; /* set when creating coroutines via
487+
coroutine.wrap */
488+
489+
unsigned propagate_error:1; /* set when propagating an error
490+
from a coroutine to its
491+
parent */
485492
};
486493

487494

src/ngx_http_lua_coroutine.c

+52-12
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)
@@ -250,7 +290,7 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
250290
int rc;
251291

252292
/* new coroutine table */
253-
lua_createtable(L, 0 /* narr */, 14 /* nrec */);
293+
lua_createtable(L, 0 /* narr */, 16 /* nrec */);
254294

255295
/* get old coroutine table */
256296
lua_getglobal(L, "coroutine");
@@ -262,6 +302,9 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
262302
lua_getfield(L, -1, "create");
263303
lua_setfield(L, -3, "_create");
264304

305+
lua_getfield(L, -1, "wrap");
306+
lua_setfield(L, -3, "_wrap");
307+
265308
lua_getfield(L, -1, "resume");
266309
lua_setfield(L, -3, "_resume");
267310

@@ -277,6 +320,9 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
277320
lua_pushcfunction(L, ngx_http_lua_coroutine_create);
278321
lua_setfield(L, -2, "__create");
279322

323+
lua_pushcfunction(L, ngx_http_lua_coroutine_wrap);
324+
lua_setfield(L, -2, "__wrap");
325+
280326
lua_pushcfunction(L, ngx_http_lua_coroutine_resume);
281327
lua_setfield(L, -2, "__resume");
282328

@@ -291,7 +337,7 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
291337
/* inject coroutine APIs */
292338
{
293339
const char buf[] =
294-
"local keys = {'create', 'yield', 'resume', 'status'}\n"
340+
"local keys = {'create', 'yield', 'resume', 'status', 'wrap'}\n"
295341
#ifdef OPENRESTY_LUAJIT
296342
"local get_req = require 'thread.exdata'\n"
297343
#else
@@ -321,24 +367,18 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
321367
"return std(...)\n"
322368
"end\n"
323369
"end\n"
324-
"local create, resume = coroutine.create, coroutine.resume\n"
325-
"coroutine.wrap = function(f)\n"
326-
"local co = create(f)\n"
327-
"return function(...) return select(2, resume(co, ...)) end\n"
328-
"end\n"
329-
"package.loaded.coroutine = coroutine";
330-
370+
"package.loaded.coroutine = coroutine"
331371
#if 0
332372
"debug.sethook(function () collectgarbage() end, 'rl', 1)"
333373
#endif
334374
;
335375

336-
rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine.wrap");
376+
rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine_api");
337377
}
338378

339379
if (rc != 0) {
340380
ngx_log_error(NGX_LOG_ERR, log, 0,
341-
"failed to load Lua code for coroutine.wrap(): %i: %s",
381+
"failed to load Lua code for coroutine_api: %i: %s",
342382
rc, lua_tostring(L, -1));
343383

344384
lua_pop(L, 1);
@@ -348,7 +388,7 @@ ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
348388
rc = lua_pcall(L, 0, 0, 0);
349389
if (rc != 0) {
350390
ngx_log_error(NGX_LOG_ERR, log, 0,
351-
"failed to run the Lua code for coroutine.wrap(): %i: %s",
391+
"failed to run the Lua code for coroutine_api: %i: %s",
352392
rc, lua_tostring(L, -1));
353393
lua_pop(L, 1);
354394
}

src/ngx_http_lua_util.c

+48-27
Original file line numberDiff line numberDiff line change
@@ -1018,8 +1018,6 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
10181018
/* set Lua VM panic handler */
10191019
lua_atpanic(L, ngx_http_lua_atpanic);
10201020

1021-
dd("ctx = %p", ctx);
1022-
10231021
NGX_LUA_EXCEPTION_TRY {
10241022

10251023
if (ctx->cur_co_ctx->thread_spawn_yielded) {
@@ -1031,19 +1029,15 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
10311029

10321030
for ( ;; ) {
10331031

1034-
dd("calling lua_resume: vm %p, nret %d", ctx->cur_co_ctx->co,
1035-
(int) nrets);
1032+
dd("ctx: %p, co: %p, co status: %d, co is_wrap: %d",
1033+
ctx, ctx->cur_co_ctx->co, ctx->cur_co_ctx->co_status,
1034+
ctx->cur_co_ctx->is_wrap);
10361035

10371036
#if (NGX_PCRE)
10381037
/* XXX: work-around to nginx regex subsystem */
10391038
old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
10401039
#endif
10411040

1042-
/* run code */
1043-
dd("ctx: %p", ctx);
1044-
dd("cur co: %p", ctx->cur_co_ctx->co);
1045-
dd("cur co status: %d", ctx->cur_co_ctx->co_status);
1046-
10471041
orig_coctx = ctx->cur_co_ctx;
10481042

10491043
#ifdef NGX_LUA_USE_ASSERT
@@ -1055,10 +1049,19 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
10551049

10561050
#if DDEBUG
10571051
if (lua_gettop(orig_coctx->co) > 0) {
1058-
dd("top elem: %s", luaL_typename(orig_coctx->co, -1));
1052+
dd("co top elem: %s", luaL_typename(orig_coctx->co, -1));
1053+
}
1054+
1055+
if (orig_coctx->propagate_error) {
1056+
dd("co propagate_error: %d", orig_coctx->propagate_error);
10591057
}
10601058
#endif
10611059

1060+
if (orig_coctx->propagate_error) {
1061+
orig_coctx->propagate_error = 0;
1062+
goto propagate_error;
1063+
}
1064+
10621065
ngx_http_lua_assert(orig_coctx->co_top + nrets
10631066
== lua_gettop(orig_coctx->co));
10641067

@@ -1203,12 +1206,6 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
12031206
next_coctx = ctx->cur_co_ctx->parent_co_ctx;
12041207
next_co = next_coctx->co;
12051208

1206-
/*
1207-
* prepare return values for coroutine.resume
1208-
* (true plus any retvals)
1209-
*/
1210-
lua_pushboolean(next_co, 1);
1211-
12121209
if (nrets) {
12131210
dd("moving %d return values to next co", nrets);
12141211
lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
@@ -1217,7 +1214,15 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
12171214
#endif
12181215
}
12191216

1220-
nrets++; /* add the true boolean value */
1217+
if (!ctx->cur_co_ctx->is_wrap) {
1218+
/*
1219+
* prepare return values for coroutine.resume
1220+
* (true plus any retvals)
1221+
*/
1222+
lua_pushboolean(next_co, 1);
1223+
lua_insert(next_co, 1);
1224+
nrets++; /* add the true boolean value */
1225+
}
12211226

12221227
ctx->cur_co_ctx = next_coctx;
12231228

@@ -1328,12 +1333,6 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
13281333

13291334
next_co = next_coctx->co;
13301335

1331-
/*
1332-
* ended successful, coroutine.resume returns true plus
1333-
* any return values
1334-
*/
1335-
lua_pushboolean(next_co, success);
1336-
13371336
if (nrets) {
13381337
lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
13391338
}
@@ -1343,7 +1342,16 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
13431342
ctx->uthreads--;
13441343
}
13451344

1346-
nrets++;
1345+
if (!ctx->cur_co_ctx->is_wrap) {
1346+
/*
1347+
* ended successful, coroutine.resume returns true plus
1348+
* any return values
1349+
*/
1350+
lua_pushboolean(next_co, success);
1351+
lua_insert(next_co, 1);
1352+
nrets++;
1353+
}
1354+
13471355
ctx->cur_co_ctx = next_coctx;
13481356

13491357
ngx_http_lua_probe_info("set parent running");
@@ -1399,6 +1407,10 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
13991407
ctx->cur_co_ctx);
14001408
trace = lua_tostring(L, -1);
14011409

1410+
propagate_error:
1411+
1412+
ngx_http_lua_assert(err != NULL && msg != NULL && trace != NULL);
1413+
14021414
if (ctx->cur_co_ctx->is_uthread) {
14031415

14041416
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -1488,16 +1500,25 @@ ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
14881500

14891501
next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
14901502

1503+
ctx->cur_co_ctx = next_coctx;
1504+
1505+
if (orig_coctx->is_wrap) {
1506+
/*
1507+
* coroutine.wrap propagates errors
1508+
* to the parent
1509+
*/
1510+
next_coctx->propagate_error = 1;
1511+
continue;
1512+
}
1513+
14911514
/*
14921515
* ended with error, coroutine.resume returns false plus
14931516
* err msg
14941517
*/
14951518
lua_pushboolean(next_co, 0);
1496-
lua_xmove(ctx->cur_co_ctx->co, next_co, 1);
1519+
lua_xmove(orig_coctx->co, next_co, 1);
14971520
nrets = 2;
14981521

1499-
ctx->cur_co_ctx = next_coctx;
1500-
15011522
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
15021523
"lua coroutine: %s: %s\n%s", err, msg, trace);
15031524

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)