Skip to content

Commit 6b3ca08

Browse files
bugfix: clean child coroutine ops when killing uthreads.
Cancel descendant coroutine operations before deleting killed user threads so delayed cosocket events cannot resume collected Lua states.
1 parent 11f9e38 commit 6b3ca08

2 files changed

Lines changed: 121 additions & 1 deletion

File tree

src/ngx_stream_lua_uthread.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,49 @@
3434
static int ngx_stream_lua_uthread_spawn(lua_State *L);
3535
static int ngx_stream_lua_uthread_wait(lua_State *L);
3636
static int ngx_stream_lua_uthread_kill(lua_State *L);
37+
static void ngx_stream_lua_uthread_cleanup_descendants(
38+
ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *parent);
39+
40+
41+
static void
42+
ngx_stream_lua_uthread_cleanup_descendants(ngx_stream_lua_ctx_t *ctx,
43+
ngx_stream_lua_co_ctx_t *parent)
44+
{
45+
ngx_uint_t i;
46+
ngx_list_part_t *part;
47+
ngx_stream_lua_co_ctx_t *coctx, *cur;
48+
49+
if (ctx->user_co_ctx == NULL) {
50+
return;
51+
}
52+
53+
part = &ctx->user_co_ctx->part;
54+
coctx = part->elts;
55+
56+
for (i = 0; /* void */; i++) {
57+
58+
if (i >= part->nelts) {
59+
if (part->next == NULL) {
60+
break;
61+
}
62+
63+
part = part->next;
64+
coctx = part->elts;
65+
i = 0;
66+
}
67+
68+
if (&coctx[i] == parent || coctx[i].is_uthread) {
69+
continue;
70+
}
71+
72+
for (cur = coctx[i].parent_co_ctx; cur; cur = cur->parent_co_ctx) {
73+
if (cur == parent) {
74+
ngx_stream_lua_cleanup_pending_operation(&coctx[i]);
75+
break;
76+
}
77+
}
78+
}
79+
}
3780

3881

3982
void
@@ -282,6 +325,7 @@ ngx_stream_lua_uthread_kill(lua_State *L)
282325

283326
default:
284327
ngx_stream_lua_cleanup_pending_operation(sub_coctx);
328+
ngx_stream_lua_uthread_cleanup_descendants(ctx, sub_coctx);
285329
ngx_stream_lua_del_thread(r, L, ctx, sub_coctx);
286330
ctx->uthreads--;
287331

t/127-uthread-kill.t

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ our $StapScript = $t::StapThread::StapScript;
77

88
repeat_each(2);
99

10-
plan tests => repeat_each() * (blocks() * 5 + 1) - 2;
10+
plan tests => repeat_each() * (blocks() * 5 - 1) - 2;
1111

1212
$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';
1313
$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';
@@ -394,3 +394,79 @@ ok_count=10
394394
--- no_error_log
395395
[error]
396396
[alert]
397+
398+
399+
400+
=== TEST 8: kill uthread with child coroutine pending cosocket read after GC
401+
--- stream_config
402+
server {
403+
listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;
404+
405+
content_by_lua_block {
406+
local body = string.rep("x", 8192)
407+
408+
ngx.print(body:sub(1, 64))
409+
ngx.flush(true)
410+
ngx.sleep(0.2)
411+
ngx.print(body:sub(65))
412+
}
413+
}
414+
--- stream_server_config
415+
lua_socket_log_errors off;
416+
417+
content_by_lua_block {
418+
local port = $TEST_NGINX_RAND_PORT_1
419+
local pinned_sockets = {}
420+
local slow_read_pending = false
421+
422+
local fast = ngx.thread.spawn(function()
423+
for _ = 1, 1000 do
424+
if slow_read_pending then
425+
break
426+
end
427+
ngx.sleep(0.001)
428+
end
429+
430+
return "ok"
431+
end)
432+
433+
local slow = ngx.thread.spawn(function()
434+
local child = coroutine.create(function()
435+
local sock = ngx.socket.tcp()
436+
pinned_sockets[#pinned_sockets + 1] = sock
437+
438+
sock:settimeout(10000)
439+
local ok, err = sock:connect("127.0.0.1", port)
440+
if not ok then
441+
return nil, err
442+
end
443+
444+
slow_read_pending = true
445+
return sock:receive(8192)
446+
end)
447+
448+
local ok, body, err = coroutine.resume(child)
449+
if not ok then
450+
return nil, body
451+
end
452+
453+
return body, err
454+
end)
455+
456+
ngx.thread.wait(fast, slow)
457+
ngx.thread.kill(fast)
458+
ngx.thread.kill(slow)
459+
460+
fast = nil
461+
slow = nil
462+
collectgarbage("collect")
463+
collectgarbage("collect")
464+
465+
ngx.sleep(0.3)
466+
467+
ngx.say("survived pinned=", #pinned_sockets)
468+
}
469+
--- stream_response
470+
survived pinned=1
471+
--- no_error_log
472+
[alert]

0 commit comments

Comments
 (0)