bugfix: when the ngx.on_abort() user thread was created but never run *and* some other pending user thread was aborted prematurely, the latter thread might get leaked. thanks Dane Knecht for the report. this regression had appeared in the v0.9.9 release.

This commit is contained in:
Yichun Zhang (agentzh) 2014-07-03 14:44:39 -07:00
Родитель 1250c5bf49
Коммит 3abfb1fc62
16 изменённых файлов: 182 добавлений и 170 удалений

Просмотреть файл

@ -98,14 +98,12 @@ static void ngx_http_lua_inject_ngx_api(lua_State *L,
static void ngx_http_lua_inject_arg_api(lua_State *L);
static int ngx_http_lua_param_get(lua_State *L);
static int ngx_http_lua_param_set(lua_State *L);
static void ngx_http_lua_del_all_threads(ngx_http_request_t *r, lua_State *L,
ngx_http_lua_ctx_t *ctx);
static ngx_int_t ngx_http_lua_output_filter(ngx_http_request_t *r,
ngx_chain_t *in);
static ngx_int_t ngx_http_lua_send_special(ngx_http_request_t *r,
ngx_uint_t flags);
static void ngx_http_lua_finalize_coroutines(ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx);
static void ngx_http_lua_finalize_threads(ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, lua_State *L);
static ngx_int_t ngx_http_lua_post_zombie_thread(ngx_http_request_t *r,
ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread);
static void ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r,
@ -368,108 +366,6 @@ ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L,
}
static void
ngx_http_lua_del_all_threads(ngx_http_request_t *r, lua_State *L,
ngx_http_lua_ctx_t *ctx)
{
int inited = 0;
int ref;
ngx_uint_t i;
ngx_list_part_t *part;
ngx_http_lua_co_ctx_t *entry_coctx;
ngx_http_lua_co_ctx_t *cc;
cc = ctx->on_abort_co_ctx;
if (cc) {
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
inited = 1;
if (cc->co_ref != LUA_NOREF) {
ngx_http_lua_probe_thread_delete(r, cc->co, ctx);
luaL_unref(L, -1, cc->co_ref);
cc->co_ref = LUA_NOREF;
if (cc->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {
ctx->uthreads--;
}
cc->co_status = NGX_HTTP_LUA_CO_DEAD;
}
ctx->on_abort_co_ctx = NULL;
}
if (ctx->user_co_ctx) {
/* release all pending user threads */
if (!inited) {
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
inited = 1;
}
part = &ctx->user_co_ctx->part;
cc = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
cc = part->elts;
i = 0;
}
ref = cc[i].co_ref;
if (ref != LUA_NOREF) {
ngx_http_lua_probe_thread_delete(r, cc[i].co, ctx);
luaL_unref(L, -1, ref);
cc[i].co_ref = LUA_NOREF;
cc[i].co_status = NGX_HTTP_LUA_CO_DEAD;
ctx->uthreads--;
#if 0
if (ctx->uthreads == 0) {
break;
}
#endif
}
}
ngx_http_lua_assert(ctx->uthreads == 0);
}
/* release the reference to the entry thread */
entry_coctx = &ctx->entry_co_ctx;
if (entry_coctx->co_ref != LUA_NOREF) {
if (!inited) {
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
inited = 1;
}
ngx_http_lua_probe_thread_delete(r, entry_coctx->co, ctx);
luaL_unref(L, -1, entry_coctx->co_ref);
entry_coctx->co_ref = LUA_NOREF;
entry_coctx->co_status = NGX_HTTP_LUA_CO_DEAD;
}
if (inited) {
lua_pop(L, 1);
}
}
u_char *
ngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len)
{
@ -905,7 +801,7 @@ ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L,
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua reset ctx");
ngx_http_lua_del_all_threads(r, L, ctx);
ngx_http_lua_finalize_threads(r, ctx, L);
#if 0
if (ctx->user_co_ctx) {
@ -1000,8 +896,7 @@ ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int forcible)
L = ngx_http_lua_get_lua_vm(r, ctx);
ngx_http_lua_finalize_coroutines(r, ctx);
ngx_http_lua_del_all_threads(r, L, ctx);
ngx_http_lua_finalize_threads(r, ctx, L);
}
@ -3150,23 +3045,39 @@ ngx_http_lua_post_thread(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
static void
ngx_http_lua_finalize_coroutines(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)
ngx_http_lua_finalize_threads(ngx_http_request_t *r,
ngx_http_lua_ctx_t *ctx, lua_State *L)
{
ngx_http_lua_co_ctx_t *cc, *coctx;
ngx_list_part_t *part;
int inited = 0, top, ref;
ngx_uint_t i;
ngx_list_part_t *part;
ngx_http_lua_co_ctx_t *cc, *coctx;
if (ctx->uthreads == 0) {
if (ngx_http_lua_is_entry_thread(ctx)) {
return;
top = lua_gettop(L);
#if 1
coctx = ctx->on_abort_co_ctx;
if (coctx && coctx->co_ref != LUA_NOREF) {
if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {
/* the on_abort thread contributes to the coctx->uthreads
* counter only when it actually starts running */
ngx_http_lua_cleanup_pending_operation(coctx);
ctx->uthreads--;
}
/* the current thread is not the entry thread */
ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);
if (ctx->entry_co_ctx.co_status == NGX_HTTP_LUA_CO_DEAD) {
return;
}
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
inited = 1;
luaL_unref(L, -1, coctx->co_ref);
coctx->co_ref = LUA_NOREF;
coctx->co_status = NGX_HTTP_LUA_CO_DEAD;
ctx->on_abort_co_ctx = NULL;
}
#endif
if (ctx->user_co_ctx) {
part = &ctx->user_co_ctx->part;
@ -3185,13 +3096,59 @@ ngx_http_lua_finalize_coroutines(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)
}
coctx = &cc[i];
ref = coctx->co_ref;
if (ref != LUA_NOREF) {
ngx_http_lua_cleanup_pending_operation(coctx);
ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);
if (!inited) {
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
inited = 1;
}
ngx_http_lua_assert(lua_gettop(L) - top == 1);
luaL_unref(L, -1, ref);
coctx->co_ref = LUA_NOREF;
coctx->co_status = NGX_HTTP_LUA_CO_DEAD;
/* TODO we could also free the user thread here */
ctx->uthreads--;
}
}
ngx_http_lua_cleanup_pending_operation(&ctx->entry_co_ctx);
ctx->user_co_ctx = NULL;
}
ngx_http_lua_assert(ctx->uthreads == 0);
coctx = &ctx->entry_co_ctx;
ref = coctx->co_ref;
if (ref != LUA_NOREF) {
ngx_http_lua_cleanup_pending_operation(coctx);
ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);
if (!inited) {
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
inited = 1;
}
ngx_http_lua_assert(lua_gettop(L) - top == 1);
luaL_unref(L, -1, coctx->co_ref);
coctx->co_ref = LUA_NOREF;
coctx->co_status = NGX_HTTP_LUA_CO_DEAD;
}
if (inited) {
lua_pop(L, 1);
}
}

Просмотреть файл

@ -162,9 +162,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request
@ -253,9 +253,9 @@ terminate 1: ok
delete thread 1
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 3
free request

Просмотреть файл

@ -153,9 +153,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request
@ -245,9 +245,9 @@ terminate 1: ok
delete thread 1
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 3
free request
@ -386,9 +386,9 @@ resolving agentzh.org
add timer 12000
expire timer 1
terminate 2: ok
delete thread 2
lua tcp resolve cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -492,9 +492,9 @@ resolving agentzh.org
add timer 12000
expire timer 1
terminate 2: ok
delete thread 2
lua udp resolve cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -582,9 +582,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -682,9 +682,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -788,9 +788,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -882,9 +882,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua udp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -975,9 +975,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -1060,9 +1060,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua req body cleanup
delete timer 12000
delete thread 2
delete thread 1
free request

Просмотреть файл

@ -179,9 +179,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request

Просмотреть файл

@ -162,9 +162,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request
@ -254,9 +254,9 @@ terminate 1: ok
delete thread 1
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 3
free request

Просмотреть файл

@ -153,9 +153,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request
@ -245,9 +245,9 @@ terminate 1: ok
delete thread 1
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 3
free request
@ -377,9 +377,9 @@ resolving agentzh.org
add timer 12000
expire timer 1
terminate 2: ok
delete thread 2
lua tcp resolve cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -473,9 +473,9 @@ resolving agentzh.org
add timer 12000
expire timer 1
terminate 2: ok
delete thread 2
lua udp resolve cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -563,9 +563,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -663,9 +663,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -769,9 +769,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -863,9 +863,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua udp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -956,9 +956,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -1041,9 +1041,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua req body cleanup
delete timer 12000
delete thread 2
delete thread 1
free request

Просмотреть файл

@ -179,9 +179,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request

Просмотреть файл

@ -204,9 +204,9 @@ add timer 100
add timer 200
expire timer 100
terminate 2: ok
delete thread 2
lua flush cleanup
delete timer 200
delete thread 2
delete thread 1
add timer 200
expire timer 200

Просмотреть файл

@ -666,9 +666,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request

Просмотреть файл

@ -153,9 +153,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request
@ -244,9 +244,9 @@ terminate 1: ok
delete thread 1
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 3
free request
@ -376,9 +376,9 @@ resolving agentzh.org
add timer 12000
expire timer 1
terminate 2: ok
delete thread 2
lua tcp resolve cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -472,9 +472,9 @@ resolving agentzh.org
add timer 12000
expire timer 1
terminate 2: ok
delete thread 2
lua udp resolve cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -562,9 +562,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -662,9 +662,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -768,9 +768,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -862,9 +862,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua udp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -954,9 +954,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua tcp socket cleanup
delete timer 12000
delete thread 2
delete thread 1
free request
@ -1039,9 +1039,9 @@ add timer 100
add timer 12000
expire timer 100
terminate 2: ok
delete thread 2
lua req body cleanup
delete timer 12000
delete thread 2
delete thread 1
free request

Просмотреть файл

@ -159,9 +159,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request
@ -249,9 +249,9 @@ terminate 1: ok
delete thread 1
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 3
free request

Просмотреть файл

@ -179,9 +179,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request

Просмотреть файл

@ -159,9 +159,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 1
free request
@ -249,9 +249,9 @@ terminate 1: ok
delete thread 1
expire timer 100
terminate 2: ok
delete thread 2
lua sleep cleanup
delete timer 1000
delete thread 2
delete thread 3
free request

Просмотреть файл

@ -20,7 +20,7 @@ our $StapScript = $t::StapThread::StapScript;
repeat_each(2);
plan tests => repeat_each() * (blocks() * 4 + 16);
plan tests => repeat_each() * (blocks() * 4 + 19);
$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';
$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';
@ -789,3 +789,58 @@ on abort called
[error]
[alert]
=== TEST 18: regsiter on_abort callback but no client abortion (2 uthreads and 1 pending)
--- config
location /t {
lua_check_client_abort on;
rewrite_by_lua '
local ok, err = ngx.on_abort(function ()
ngx.log(ngx.NOTICE, "on abort called")
end)
if not ok then
error("cannot set on_abort: " .. err)
end
ngx.thread.spawn(function ()
ngx.sleep(0.1)
ngx.say("done")
ngx.exit(200)
end)
ngx.thread.spawn(function ()
ngx.sleep(100)
end)
';
content_by_lua return;
}
--- request
GET /t
--- stap2 eval: $::StapScript
--- stap eval: $::GCScript
--- stap_out
create 2 in 1
create 3 in 1
spawn user thread 3 in 1
create 4 in 1
spawn user thread 4 in 1
terminate 1: ok
delete thread 1
terminate 3: ok
lua req cleanup
delete thread 2
delete thread 3
delete thread 4
--- wait: 0.5
--- response_body
done
--- no_error_log
[error]
client prematurely closed connection
on abort called
main handler done

Просмотреть файл

@ -1386,9 +1386,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 3: ok
delete thread 3
lua sleep cleanup
delete timer 1000
delete thread 3
delete thread 2|create 2 in 1
terminate 1: ok
delete thread 1
@ -1399,9 +1399,9 @@ add timer 1000
free request
expire timer 100
terminate 3: ok
delete thread 3
lua sleep cleanup
delete timer 1000
delete thread 3
delete thread 2)$
--- response_body

Просмотреть файл

@ -1305,9 +1305,9 @@ add timer 100
add timer 1000
expire timer 100
terminate 3: ok
delete thread 3
lua sleep cleanup
delete timer 1000
delete thread 3
delete thread 2
terminate 1: ok
delete thread 1