1# vim:set ft= ts=4 sw=4 et fdm=marker:
2
3use Test::Nginx::Socket::Lua;
4use t::StapThread;
5
6our $GCScript = $t::StapThread::GCScript;
7our $StapScript = $t::StapThread::StapScript;
8
9repeat_each(2);
10
11plan tests => repeat_each() * (blocks() * 4);
12
13$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';
14$ENV{TEST_NGINX_MEMCACHED_PORT} ||= '11211';
15$ENV{TEST_NGINX_REDIS_PORT} ||= '6379';
16
17#no_shuffle();
18no_long_string();
19run_tests();
20
21__DATA__
22
23=== TEST 1: exit in user thread (entry thread is still pending to run)
24--- config
25    location /lua {
26        access_by_lua '
27            local function f()
28                ngx.say("hello in thread")
29                ngx.exit(0)
30            end
31
32            ngx.say("before")
33            ngx.thread.spawn(f)
34            ngx.say("after")
35            ngx.sleep(1)
36            ngx.say("end")
37        ';
38    }
39--- request
40GET /lua
41--- stap2 eval: $::StapScript
42--- stap eval
43<<'_EOC_' . $::GCScript;
44
45global timers
46
47M(timer-add) {
48    if ($arg2 == 1000) {
49        timers[$arg1] = $arg2
50        printf("add timer %d\n", $arg2)
51    }
52}
53
54M(timer-del) {
55    tm = timers[$arg1]
56    if (tm == 1000) {
57        printf("delete timer %d\n", tm)
58        delete timers[$arg1]
59    }
60}
61
62M(timer-expire) {
63    tm = timers[$arg1]
64    if (tm == 1000) {
65        printf("expire timer %d\n", timers[$arg1])
66        delete timers[$arg1]
67    }
68}
69_EOC_
70
71--- stap_out
72create 2 in 1
73spawn user thread 2 in 1
74terminate 2: ok
75delete thread 2
76delete thread 1
77
78--- response_body
79before
80hello in thread
81--- no_error_log
82[error]
83
84
85
86=== TEST 2: exit in user thread (entry thread is still pending on ngx.sleep)
87--- config
88    location /lua {
89        access_by_lua '
90            local function f()
91                ngx.say("hello in thread")
92                ngx.sleep(0.1)
93                ngx.exit(0)
94            end
95
96            ngx.say("before")
97            ngx.thread.spawn(f)
98            ngx.say("after")
99            ngx.sleep(1)
100            ngx.say("end")
101        ';
102    }
103--- request
104GET /lua
105--- stap2 eval: $::StapScript
106--- stap eval
107<<'_EOC_' . $::GCScript;
108
109global timers
110
111F(ngx_http_free_request) {
112    println("free request")
113}
114
115M(timer-add) {
116    if ($arg2 == 1000 || $arg2 == 100) {
117        timers[$arg1] = $arg2
118        printf("add timer %d\n", $arg2)
119    }
120}
121
122M(timer-del) {
123    tm = timers[$arg1]
124    if (tm == 1000 || tm == 100) {
125        printf("delete timer %d\n", tm)
126        delete timers[$arg1]
127    }
128    /*
129    if (tm == 1000) {
130        print_ubacktrace()
131    }
132    */
133}
134
135M(timer-expire) {
136    tm = timers[$arg1]
137    if (tm == 1000 || tm == 100) {
138        printf("expire timer %d\n", timers[$arg1])
139        delete timers[$arg1]
140    }
141}
142
143F(ngx_http_lua_sleep_cleanup) {
144    println("lua sleep cleanup")
145}
146_EOC_
147
148--- stap_out
149create 2 in 1
150spawn user thread 2 in 1
151add timer 100
152add timer 1000
153expire timer 100
154terminate 2: ok
155delete thread 2
156lua sleep cleanup
157delete timer 1000
158delete thread 1
159free request
160
161--- wait: 0.1
162--- response_body
163before
164hello in thread
165after
166--- no_error_log
167[error]
168
169
170
171=== TEST 3: exit in a user thread (another user thread is still pending on ngx.sleep)
172--- config
173    location /lua {
174        access_by_lua '
175            local function f()
176                ngx.sleep(0.1)
177                ngx.say("f")
178                ngx.exit(0)
179            end
180
181            local function g()
182                ngx.sleep(1)
183                ngx.say("g")
184            end
185
186            ngx.thread.spawn(f)
187            ngx.thread.spawn(g)
188            ngx.say("end")
189        ';
190    }
191--- request
192GET /lua
193--- stap2 eval: $::StapScript
194--- stap eval
195<<'_EOC_' . $::GCScript;
196
197global timers
198
199F(ngx_http_free_request) {
200    println("free request")
201}
202
203M(timer-add) {
204    if ($arg2 == 1000 || $arg2 == 100) {
205        timers[$arg1] = $arg2
206        printf("add timer %d\n", $arg2)
207    }
208}
209
210M(timer-del) {
211    tm = timers[$arg1]
212    if (tm == 1000 || tm == 100) {
213        printf("delete timer %d\n", tm)
214        delete timers[$arg1]
215    }
216    /*
217    if (tm == 1000) {
218        print_ubacktrace()
219    }
220    */
221}
222
223M(timer-expire) {
224    tm = timers[$arg1]
225    if (tm == 1000 || tm == 100) {
226        printf("expire timer %d\n", timers[$arg1])
227        delete timers[$arg1]
228    }
229}
230
231F(ngx_http_lua_sleep_cleanup) {
232    println("lua sleep cleanup")
233}
234_EOC_
235
236--- stap_out
237create 2 in 1
238spawn user thread 2 in 1
239add timer 100
240create 3 in 1
241spawn user thread 3 in 1
242add timer 1000
243terminate 1: ok
244delete thread 1
245expire timer 100
246terminate 2: ok
247delete thread 2
248lua sleep cleanup
249delete timer 1000
250delete thread 3
251free request
252
253--- response_body
254end
255f
256--- no_error_log
257[error]
258
259
260
261=== TEST 4: exit in user thread (entry already quits)
262--- config
263    location /lua {
264        access_by_lua '
265            local function f()
266                ngx.sleep(0.1)
267                ngx.say("exiting the user thread")
268                ngx.exit(0)
269            end
270
271            ngx.say("before")
272            ngx.thread.spawn(f)
273            ngx.say("after")
274        ';
275    }
276--- request
277GET /lua
278--- stap2 eval: $::StapScript
279--- stap eval: $::GCScript
280--- stap_out
281create 2 in 1
282spawn user thread 2 in 1
283terminate 1: ok
284delete thread 1
285terminate 2: ok
286delete thread 2
287
288--- wait: 0.1
289--- response_body
290before
291after
292exiting the user thread
293--- no_error_log
294[error]
295
296
297
298=== TEST 5: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.tcp)
299--- config
300    location /lua {
301        resolver 127.0.0.2:12345;
302        resolver_timeout 12s;
303        access_by_lua '
304            local function f()
305                ngx.say("hello in thread")
306                ngx.sleep(0.001)
307                ngx.exit(0)
308            end
309
310            ngx.say("before")
311            ngx.thread.spawn(f)
312            ngx.say("after")
313            local sock = ngx.socket.tcp()
314            local ok, err = sock:connect("agentzh.org", 80)
315            if not ok then
316                ngx.say("failed to connect: ", err)
317                return
318            end
319            ngx.say("end")
320        ';
321    }
322--- request
323GET /lua
324--- stap2 eval: $::StapScript
325--- stap eval
326<<'_EOC_' . $::GCScript;
327
328global timers
329
330F(ngx_http_free_request) {
331    println("free request")
332}
333
334F(ngx_resolve_name) {
335    printf("resolving %s\n", user_string_n($ctx->name->data, $ctx->name->len))
336}
337
338M(timer-add) {
339    if ($arg2 == 12000 || $arg2 == 1) {
340        timers[$arg1] = $arg2
341        printf("add timer %d\n", $arg2)
342    }
343}
344
345M(timer-del) {
346    tm = timers[$arg1]
347    if (tm == 12000 || tm == 1) {
348        printf("delete timer %d\n", tm)
349        delete timers[$arg1]
350    }
351    /*
352    if (tm == 12000) {
353        print_ubacktrace()
354    }
355    */
356}
357
358M(timer-expire) {
359    tm = timers[$arg1]
360    if (tm == 12000 || tm == 1) {
361        printf("expire timer %d\n", timers[$arg1])
362        delete timers[$arg1]
363    }
364}
365
366F(ngx_http_lua_tcp_resolve_cleanup) {
367    println("lua tcp resolve cleanup")
368}
369_EOC_
370
371--- stap_out
372create 2 in 1
373spawn user thread 2 in 1
374add timer 1
375resolving agentzh.org
376add timer 12000
377expire timer 1
378terminate 2: ok
379delete thread 2
380lua tcp resolve cleanup
381delete timer 12000
382delete thread 1
383free request
384
385--- response_body
386before
387hello in thread
388after
389--- no_error_log
390[error]
391
392
393
394=== TEST 6: exit in user thread (entry thread is still pending on the DNS resolver for ngx.socket.udp)
395--- config
396    location /lua {
397        resolver 127.0.0.2:12345;
398        resolver_timeout 12s;
399        access_by_lua '
400            local function f()
401                ngx.say("hello in thread")
402                ngx.sleep(0.001)
403                ngx.exit(0)
404            end
405
406            ngx.say("before")
407            ngx.thread.spawn(f)
408            ngx.say("after")
409            local sock = ngx.socket.udp()
410            local ok, err = sock:setpeername("agentzh.org", 80)
411            if not ok then
412                ngx.say("failed to connect: ", err)
413                return
414            end
415            ngx.say("end")
416        ';
417    }
418--- request
419GET /lua
420--- stap2 eval: $::StapScript
421--- stap eval
422<<'_EOC_' . $::GCScript;
423
424global timers
425
426F(ngx_http_free_request) {
427    println("free request")
428}
429
430F(ngx_resolve_name) {
431    printf("resolving %s\n", user_string_n($ctx->name->data, $ctx->name->len))
432}
433
434M(timer-add) {
435    if ($arg2 == 12000 || $arg2 == 1) {
436        timers[$arg1] = $arg2
437        printf("add timer %d\n", $arg2)
438    }
439}
440
441M(timer-del) {
442    tm = timers[$arg1]
443    if (tm == 12000 || tm == 1) {
444        printf("delete timer %d\n", tm)
445        delete timers[$arg1]
446    }
447    /*
448    if (tm == 12000) {
449        print_ubacktrace()
450    }
451    */
452}
453
454M(timer-expire) {
455    tm = timers[$arg1]
456    if (tm == 12000 || tm == 1) {
457        printf("expire timer %d\n", timers[$arg1])
458        delete timers[$arg1]
459    }
460}
461
462F(ngx_http_lua_udp_resolve_cleanup) {
463    println("lua udp resolve cleanup")
464}
465_EOC_
466
467--- stap_out
468create 2 in 1
469spawn user thread 2 in 1
470add timer 1
471resolving agentzh.org
472add timer 12000
473expire timer 1
474terminate 2: ok
475delete thread 2
476lua udp resolve cleanup
477delete timer 12000
478delete thread 1
479free request
480
481--- response_body
482before
483hello in thread
484after
485--- no_error_log
486[error]
487
488
489
490=== TEST 7: exit in user thread (entry thread is still pending on tcpsock:connect)
491--- config
492    location /lua {
493        access_by_lua '
494            local function f()
495                ngx.say("hello in thread")
496                ngx.sleep(0.1)
497                ngx.exit(0)
498            end
499
500            ngx.say("before")
501            ngx.thread.spawn(f)
502            ngx.say("after")
503            local sock = ngx.socket.tcp()
504            sock:settimeout(12000)
505            local ok, err = sock:connect("127.0.0.2", 12345)
506            if not ok then
507                ngx.say("failed to connect: ", err)
508                return
509            end
510            ngx.say("end")
511        ';
512    }
513--- request
514GET /lua
515--- stap2 eval: $::StapScript
516--- stap eval
517<<'_EOC_' . $::GCScript;
518
519global timers
520
521F(ngx_http_free_request) {
522    println("free request")
523}
524
525M(timer-add) {
526    if ($arg2 == 12000 || $arg2 == 100) {
527        timers[$arg1] = $arg2
528        printf("add timer %d\n", $arg2)
529    }
530}
531
532M(timer-del) {
533    tm = timers[$arg1]
534    if (tm == 12000 || tm == 100) {
535        printf("delete timer %d\n", tm)
536        delete timers[$arg1]
537    }
538    /*
539    if (tm == 12000) {
540        print_ubacktrace()
541    }
542    */
543}
544
545M(timer-expire) {
546    tm = timers[$arg1]
547    if (tm == 12000 || tm == 100) {
548        printf("expire timer %d\n", timers[$arg1])
549        delete timers[$arg1]
550    }
551}
552
553F(ngx_http_lua_coctx_cleanup) {
554    println("lua tcp socket cleanup")
555}
556_EOC_
557
558--- stap_out
559create 2 in 1
560spawn user thread 2 in 1
561add timer 100
562add timer 12000
563expire timer 100
564terminate 2: ok
565delete thread 2
566lua tcp socket cleanup
567delete timer 12000
568delete thread 1
569free request
570
571--- wait: 0.1
572--- response_body
573before
574hello in thread
575after
576--- no_error_log
577[error]
578
579
580
581=== TEST 8: exit in user thread (entry thread is still pending on tcpsock:receive)
582--- config
583    location /lua {
584        access_by_lua '
585            local function f()
586                ngx.say("hello in thread")
587                ngx.sleep(0.1)
588                ngx.exit(0)
589            end
590
591            ngx.say("before")
592            ngx.thread.spawn(f)
593            ngx.say("after")
594            local sock = ngx.socket.tcp()
595
596            local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
597            if not ok then
598                ngx.say("failed to connect: ", err)
599                return
600            end
601
602            local bytes, ok = sock:send("blpop not_exists 2\\r\\n")
603            if not bytes then
604                ngx.say("failed to send: ", err)
605                return
606            end
607
608            sock:settimeout(12000)
609
610            local data, err = sock:receive()
611            if not data then
612                ngx.say("failed to receive: ", err)
613                return
614            end
615
616            ngx.say("end")
617        ';
618    }
619--- request
620GET /lua
621--- stap2 eval: $::StapScript
622--- stap eval
623<<'_EOC_' . $::GCScript;
624
625global timers
626
627F(ngx_http_free_request) {
628    println("free request")
629}
630
631M(timer-add) {
632    if ($arg2 == 12000 || $arg2 == 100) {
633        timers[$arg1] = $arg2
634        printf("add timer %d\n", $arg2)
635    }
636}
637
638M(timer-del) {
639    tm = timers[$arg1]
640    if (tm == 12000 || tm == 100) {
641        printf("delete timer %d\n", tm)
642        delete timers[$arg1]
643    }
644}
645
646M(timer-expire) {
647    tm = timers[$arg1]
648    if (tm == 12000 || tm == 100) {
649        printf("expire timer %d\n", timers[$arg1])
650        delete timers[$arg1]
651    }
652}
653
654F(ngx_http_lua_coctx_cleanup) {
655    println("lua tcp socket cleanup")
656}
657_EOC_
658
659--- stap_out
660create 2 in 1
661spawn user thread 2 in 1
662add timer 100
663add timer 12000
664expire timer 100
665terminate 2: ok
666delete thread 2
667lua tcp socket cleanup
668delete timer 12000
669delete thread 1
670free request
671
672--- response_body
673before
674hello in thread
675after
676--- no_error_log
677[error]
678
679
680
681=== TEST 9: exit in user thread (entry thread is still pending on tcpsock:receiveuntil's iterator)
682--- config
683    location /lua {
684        access_by_lua '
685            local function f()
686                ngx.say("hello in thread")
687                ngx.sleep(0.1)
688                ngx.exit(0)
689            end
690
691            ngx.say("before")
692            ngx.thread.spawn(f)
693            ngx.say("after")
694            local sock = ngx.socket.tcp()
695
696            local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
697            if not ok then
698                ngx.say("failed to connect: ", err)
699                return
700            end
701
702            local bytes, ok = sock:send("blpop not_exists 2\\r\\n")
703            if not bytes then
704                ngx.say("failed to send: ", err)
705                return
706            end
707
708            local it, err = sock:receiveuntil("\\r\\n")
709            if not it then
710                ngx.say("failed to receive until: ", err)
711                return
712            end
713
714            sock:settimeout(12000)
715
716            local data, err = it()
717            if not data then
718                ngx.say("failed to receive: ", err)
719                return
720            end
721
722            ngx.say("end")
723        ';
724    }
725--- request
726GET /lua
727--- stap2 eval: $::StapScript
728--- stap eval
729<<'_EOC_' . $::GCScript;
730
731global timers
732
733F(ngx_http_free_request) {
734    println("free request")
735}
736
737M(timer-add) {
738    if ($arg2 == 12000 || $arg2 == 100) {
739        timers[$arg1] = $arg2
740        printf("add timer %d\n", $arg2)
741    }
742}
743
744M(timer-del) {
745    tm = timers[$arg1]
746    if (tm == 12000 || tm == 100) {
747        printf("delete timer %d\n", tm)
748        delete timers[$arg1]
749    }
750}
751
752M(timer-expire) {
753    tm = timers[$arg1]
754    if (tm == 12000 || tm == 100) {
755        printf("expire timer %d\n", timers[$arg1])
756        delete timers[$arg1]
757    }
758}
759
760F(ngx_http_lua_coctx_cleanup) {
761    println("lua tcp socket cleanup")
762}
763_EOC_
764
765--- stap_out
766create 2 in 1
767spawn user thread 2 in 1
768add timer 100
769add timer 12000
770expire timer 100
771terminate 2: ok
772delete thread 2
773lua tcp socket cleanup
774delete timer 12000
775delete thread 1
776free request
777
778--- response_body
779before
780hello in thread
781after
782--- no_error_log
783[error]
784
785
786
787=== TEST 10: exit in user thread (entry thread is still pending on udpsock:receive)
788--- config
789    location /lua {
790        access_by_lua '
791            local function f()
792                ngx.say("hello in thread")
793                ngx.sleep(0.1)
794                ngx.exit(0)
795            end
796
797            ngx.say("before")
798            ngx.thread.spawn(f)
799            ngx.say("after")
800            local sock = ngx.socket.udp()
801
802            local ok, err = sock:setpeername("8.8.8.8", 12345)
803            if not ok then
804                ngx.say("failed to connect: ", err)
805                return
806            end
807
808            sock:settimeout(12000)
809
810            local data, err = sock:receive()
811            if not data then
812                ngx.say("failed to receive: ", err)
813                return
814            end
815
816            ngx.say("end")
817        ';
818    }
819--- request
820GET /lua
821--- stap2 eval: $::StapScript
822--- stap eval
823<<'_EOC_' . $::GCScript;
824
825global timers
826
827F(ngx_http_free_request) {
828    println("free request")
829}
830
831M(timer-add) {
832    if ($arg2 == 12000 || $arg2 == 100) {
833        timers[$arg1] = $arg2
834        printf("add timer %d\n", $arg2)
835    }
836}
837
838M(timer-del) {
839    tm = timers[$arg1]
840    if (tm == 12000 || tm == 100) {
841        printf("delete timer %d\n", tm)
842        delete timers[$arg1]
843    }
844}
845
846M(timer-expire) {
847    tm = timers[$arg1]
848    if (tm == 12000 || tm == 100) {
849        printf("expire timer %d\n", timers[$arg1])
850        delete timers[$arg1]
851    }
852}
853
854F(ngx_http_lua_udp_socket_cleanup) {
855    println("lua udp socket cleanup")
856}
857_EOC_
858
859--- stap_out
860create 2 in 1
861spawn user thread 2 in 1
862add timer 100
863add timer 12000
864expire timer 100
865terminate 2: ok
866delete thread 2
867lua udp socket cleanup
868delete timer 12000
869delete thread 1
870free request
871
872--- wait: 0.1
873--- response_body
874before
875hello in thread
876after
877--- no_error_log
878[error]
879
880
881
882=== TEST 11: exit in user thread (entry thread is still pending on reqsock:receive)
883--- config
884    location /lua {
885        access_by_lua '
886            local function f()
887                ngx.say("hello in thread")
888                ngx.sleep(0.1)
889                ngx.exit(0)
890            end
891
892            ngx.say("before")
893            ngx.thread.spawn(f)
894            ngx.say("after")
895            local sock = ngx.req.socket()
896
897            sock:settimeout(12000)
898
899            local data, err = sock:receive(1024)
900            if not data then
901                ngx.say("failed to receive: ", err)
902                return
903            end
904
905            ngx.say("end")
906        ';
907    }
908--- request
909POST /lua
910
911--- more_headers
912Content-Length: 1024
913
914--- stap2 eval: $::StapScript
915--- stap eval
916<<'_EOC_' . $::GCScript;
917
918global timers
919
920F(ngx_http_free_request) {
921    println("free request")
922}
923
924M(timer-add) {
925    if ($arg2 == 12000 || $arg2 == 100) {
926        timers[$arg1] = $arg2
927        printf("add timer %d\n", $arg2)
928    }
929}
930
931M(timer-del) {
932    tm = timers[$arg1]
933    if (tm == 12000 || tm == 100) {
934        printf("delete timer %d\n", tm)
935        delete timers[$arg1]
936    }
937}
938
939M(timer-expire) {
940    tm = timers[$arg1]
941    if (tm == 12000 || tm == 100) {
942        printf("expire timer %d\n", timers[$arg1])
943        delete timers[$arg1]
944    }
945}
946
947F(ngx_http_lua_coctx_cleanup) {
948    println("lua tcp socket cleanup")
949}
950_EOC_
951
952--- stap_out
953create 2 in 1
954spawn user thread 2 in 1
955add timer 100
956add timer 12000
957expire timer 100
958terminate 2: ok
959delete thread 2
960lua tcp socket cleanup
961delete timer 12000
962delete thread 1
963free request
964
965--- wait: 0.1
966--- response_body
967before
968hello in thread
969after
970--- no_error_log
971[error]
972
973
974
975=== TEST 12: exit in user thread (entry thread is still pending on ngx.req.read_body)
976--- config
977    location /lua {
978        client_body_timeout 12000ms;
979        access_by_lua '
980            local function f()
981                ngx.say("hello in thread")
982                ngx.sleep(0.1)
983                ngx.exit(0)
984            end
985
986            ngx.say("before")
987            ngx.thread.spawn(f)
988            ngx.say("after")
989
990            ngx.req.read_body()
991
992            ngx.say("end")
993        ';
994    }
995--- request
996POST /lua
997--- more_headers
998Content-Length: 1024
999--- stap2 eval: $::StapScript
1000--- stap eval
1001<<'_EOC_' . $::GCScript;
1002
1003global timers
1004
1005F(ngx_http_free_request) {
1006    println("free request")
1007}
1008
1009M(timer-add) {
1010    if ($arg2 == 12000 || $arg2 == 100) {
1011        timers[$arg1] = $arg2
1012        printf("add timer %d\n", $arg2)
1013    }
1014}
1015
1016M(timer-del) {
1017    tm = timers[$arg1]
1018    if (tm == 12000 || tm == 100) {
1019        printf("delete timer %d\n", tm)
1020        delete timers[$arg1]
1021    }
1022}
1023
1024M(timer-expire) {
1025    tm = timers[$arg1]
1026    if (tm == 12000 || tm == 100) {
1027        printf("expire timer %d\n", timers[$arg1])
1028        delete timers[$arg1]
1029    }
1030}
1031
1032F(ngx_http_lua_req_body_cleanup) {
1033    println("lua req body cleanup")
1034}
1035_EOC_
1036
1037--- stap_out
1038create 2 in 1
1039spawn user thread 2 in 1
1040add timer 100
1041add timer 12000
1042expire timer 100
1043terminate 2: ok
1044delete thread 2
1045lua req body cleanup
1046delete timer 12000
1047delete thread 1
1048free request
1049
1050--- wait: 0.1
1051--- response_body
1052before
1053hello in thread
1054after
1055--- no_error_log
1056[error]
1057
1058
1059
1060=== TEST 13: exit in user thread (entry thread is still pending on ngx.location.capture), with pending output
1061--- config
1062    location /lua {
1063        client_body_timeout 12000ms;
1064        access_by_lua '
1065            local function f()
1066                ngx.say("hello in thread")
1067                ngx.sleep(0.1)
1068                ngx.exit(0)
1069            end
1070
1071            ngx.say("before")
1072            ngx.thread.spawn(f)
1073            ngx.say("after")
1074
1075            ngx.location.capture("/sleep")
1076
1077            ngx.say("end")
1078        ';
1079    }
1080
1081    location = /sleep {
1082        echo_sleep 0.2;
1083    }
1084--- request
1085POST /lua
1086--- stap2 eval: $::StapScript
1087--- stap eval
1088<<'_EOC_' . $::GCScript;
1089
1090global timers
1091
1092F(ngx_http_free_request) {
1093    println("free request")
1094}
1095
1096M(timer-add) {
1097    if ($arg2 == 200 || $arg2 == 100) {
1098        timers[$arg1] = $arg2
1099        printf("add timer %d\n", $arg2)
1100    }
1101}
1102
1103M(timer-del) {
1104    tm = timers[$arg1]
1105    if (tm == 200 || tm == 100) {
1106        printf("delete timer %d\n", tm)
1107        delete timers[$arg1]
1108    }
1109}
1110
1111M(timer-expire) {
1112    tm = timers[$arg1]
1113    if (tm == 200 || tm == 100) {
1114        printf("expire timer %d\n", timers[$arg1])
1115        delete timers[$arg1]
1116    }
1117}
1118_EOC_
1119
1120--- stap_out
1121create 2 in 1
1122spawn user thread 2 in 1
1123add timer 100
1124add timer 200
1125expire timer 100
1126terminate 2: fail
1127expire timer 200
1128terminate 1: ok
1129delete thread 2
1130delete thread 1
1131free request
1132
1133--- wait: 0.1
1134--- response_body
1135before
1136hello in thread
1137after
1138end
1139--- error_log
1140attempt to abort with pending subrequests
1141
1142
1143
1144=== TEST 14: exit in user thread (entry thread is still pending on ngx.location.capture), without pending output
1145--- config
1146    location /lua {
1147        client_body_timeout 12000ms;
1148        access_by_lua '
1149            local function f()
1150                ngx.sleep(0.1)
1151                ngx.exit(0)
1152            end
1153
1154            ngx.thread.spawn(f)
1155
1156            ngx.location.capture("/sleep")
1157            ngx.say("end")
1158        ';
1159    }
1160
1161    location = /sleep {
1162        echo_sleep 0.2;
1163    }
1164--- request
1165POST /lua
1166--- stap2 eval: $::StapScript
1167--- stap eval
1168<<'_EOC_' . $::GCScript;
1169
1170global timers
1171
1172F(ngx_http_free_request) {
1173    println("free request")
1174}
1175
1176M(timer-add) {
1177    if ($arg2 == 200 || $arg2 == 100) {
1178        timers[$arg1] = $arg2
1179        printf("add timer %d\n", $arg2)
1180    }
1181}
1182
1183M(timer-del) {
1184    tm = timers[$arg1]
1185    if (tm == 200 || tm == 100) {
1186        printf("delete timer %d\n", tm)
1187        delete timers[$arg1]
1188    }
1189}
1190
1191M(timer-expire) {
1192    tm = timers[$arg1]
1193    if (tm == 200 || tm == 100) {
1194        printf("expire timer %d\n", timers[$arg1])
1195        delete timers[$arg1]
1196    }
1197}
1198
1199F(ngx_http_lua_post_subrequest) {
1200    printf("post subreq %s\n", ngx_http_req_uri($r))
1201}
1202
1203_EOC_
1204
1205--- stap_out
1206create 2 in 1
1207spawn user thread 2 in 1
1208add timer 100
1209add timer 200
1210expire timer 100
1211terminate 2: fail
1212expire timer 200
1213post subreq /sleep
1214terminate 1: ok
1215delete thread 2
1216delete thread 1
1217free request
1218
1219--- wait: 0.1
1220--- response_body
1221end
1222--- error_log
1223attempt to abort with pending subrequests
1224
1225
1226
1227=== TEST 15: exit in user thread (entry thread is still pending on ngx.location.capture_multi), without pending output
1228--- config
1229    location /lua {
1230        client_body_timeout 12000ms;
1231        access_by_lua '
1232            local function f()
1233                ngx.sleep(0.1)
1234                ngx.exit(0)
1235            end
1236
1237            ngx.thread.spawn(f)
1238
1239            ngx.location.capture_multi{
1240                {"/echo"},
1241                {"/sleep"}
1242            }
1243            ngx.say("end")
1244        ';
1245    }
1246
1247    location = /echo {
1248        echo hello;
1249    }
1250
1251    location = /sleep {
1252        echo_sleep 0.2;
1253    }
1254--- request
1255POST /lua
1256--- stap2 eval: $::StapScript
1257--- stap eval
1258<<'_EOC_' . $::GCScript;
1259
1260global timers
1261
1262F(ngx_http_free_request) {
1263    println("free request")
1264}
1265
1266M(timer-add) {
1267    if ($arg2 == 200 || $arg2 == 100) {
1268        timers[$arg1] = $arg2
1269        printf("add timer %d\n", $arg2)
1270    }
1271}
1272
1273M(timer-del) {
1274    tm = timers[$arg1]
1275    if (tm == 200 || tm == 100) {
1276        printf("delete timer %d\n", tm)
1277        delete timers[$arg1]
1278    }
1279}
1280
1281M(timer-expire) {
1282    tm = timers[$arg1]
1283    if (tm == 200 || tm == 100) {
1284        printf("expire timer %d\n", timers[$arg1])
1285        delete timers[$arg1]
1286    }
1287}
1288
1289F(ngx_http_lua_post_subrequest) {
1290    printf("post subreq %s\n", ngx_http_req_uri($r))
1291}
1292_EOC_
1293
1294--- stap_out
1295create 2 in 1
1296spawn user thread 2 in 1
1297add timer 100
1298post subreq /echo
1299add timer 200
1300expire timer 100
1301terminate 2: fail
1302expire timer 200
1303post subreq /sleep
1304terminate 1: ok
1305delete thread 2
1306delete thread 1
1307free request
1308
1309--- wait: 0.1
1310--- response_body
1311end
1312--- error_log
1313attempt to abort with pending subrequests
1314