Line data Source code
1 :
2 : /*
3 : * Copyright (C) Yichun Zhang (agentzh)
4 : */
5 :
6 :
7 : #ifndef DDEBUG
8 : #define DDEBUG 0
9 : #endif
10 : #include "ddebug.h"
11 :
12 :
13 : #include "ngx_http_lua_uthread.h"
14 : #include "ngx_http_lua_coroutine.h"
15 : #include "ngx_http_lua_util.h"
16 : #include "ngx_http_lua_probe.h"
17 :
18 :
19 : #if 1
20 : #undef ngx_http_lua_probe_info
21 : #define ngx_http_lua_probe_info(msg)
22 : #endif
23 :
24 :
25 : static int ngx_http_lua_uthread_spawn(lua_State *L);
26 : static int ngx_http_lua_uthread_wait(lua_State *L);
27 : static int ngx_http_lua_uthread_kill(lua_State *L);
28 :
29 :
30 : void
31 18 : ngx_http_lua_inject_uthread_api(ngx_log_t *log, lua_State *L)
32 : {
33 : /* new thread table */
34 18 : lua_createtable(L, 0 /* narr */, 3 /* nrec */);
35 :
36 18 : lua_pushcfunction(L, ngx_http_lua_uthread_spawn);
37 18 : lua_setfield(L, -2, "spawn");
38 :
39 18 : lua_pushcfunction(L, ngx_http_lua_uthread_wait);
40 18 : lua_setfield(L, -2, "wait");
41 :
42 18 : lua_pushcfunction(L, ngx_http_lua_uthread_kill);
43 18 : lua_setfield(L, -2, "kill");
44 :
45 18 : lua_setfield(L, -2, "thread");
46 18 : }
47 :
48 :
49 : static int
50 0 : ngx_http_lua_uthread_spawn(lua_State *L)
51 : {
52 : int n;
53 : ngx_http_request_t *r;
54 : ngx_http_lua_ctx_t *ctx;
55 0 : ngx_http_lua_co_ctx_t *coctx = NULL;
56 :
57 0 : n = lua_gettop(L);
58 :
59 0 : r = ngx_http_lua_get_req(L);
60 0 : if (r == NULL) {
61 0 : return luaL_error(L, "no request found");
62 : }
63 :
64 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
65 0 : if (ctx == NULL) {
66 0 : return luaL_error(L, "no request ctx found");
67 : }
68 :
69 0 : ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx);
70 :
71 : /* anchor the newly created coroutine into the Lua registry */
72 :
73 0 : lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
74 0 : lua_rawget(L, LUA_REGISTRYINDEX);
75 0 : lua_pushvalue(L, -2);
76 0 : coctx->co_ref = luaL_ref(L, -2);
77 0 : lua_pop(L, 1);
78 :
79 0 : if (n > 1) {
80 0 : lua_replace(L, 1);
81 0 : lua_xmove(L, coctx->co, n - 1);
82 : }
83 :
84 0 : coctx->is_uthread = 1;
85 0 : ctx->uthreads++;
86 :
87 0 : coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
88 0 : ctx->co_op = NGX_HTTP_LUA_USER_THREAD_RESUME;
89 :
90 0 : ctx->cur_co_ctx->thread_spawn_yielded = 1;
91 :
92 0 : if (ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx) != NGX_OK) {
93 0 : return luaL_error(L, "no memory");
94 : }
95 :
96 0 : coctx->parent_co_ctx = ctx->cur_co_ctx;
97 0 : ctx->cur_co_ctx = coctx;
98 :
99 : ngx_http_lua_probe_user_thread_spawn(r, L, coctx->co);
100 :
101 : dd("yielding with arg %s, top=%d, index-1:%s", luaL_typename(L, -1),
102 : (int) lua_gettop(L), luaL_typename(L, 1));
103 0 : return lua_yield(L, 1);
104 : }
105 :
106 :
107 : static int
108 0 : ngx_http_lua_uthread_wait(lua_State *L)
109 : {
110 : int i, nargs, nrets;
111 : lua_State *sub_co;
112 : ngx_http_request_t *r;
113 : ngx_http_lua_ctx_t *ctx;
114 : ngx_http_lua_co_ctx_t *coctx, *sub_coctx;
115 :
116 0 : r = ngx_http_lua_get_req(L);
117 0 : if (r == NULL) {
118 0 : return luaL_error(L, "no request found");
119 : }
120 :
121 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
122 0 : if (ctx == NULL) {
123 0 : return luaL_error(L, "no request ctx found");
124 : }
125 :
126 0 : ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
127 : | NGX_HTTP_LUA_CONTEXT_ACCESS
128 : | NGX_HTTP_LUA_CONTEXT_CONTENT
129 : | NGX_HTTP_LUA_CONTEXT_TIMER
130 : | NGX_HTTP_LUA_CONTEXT_SSL_CERT);
131 :
132 0 : coctx = ctx->cur_co_ctx;
133 :
134 0 : nargs = lua_gettop(L);
135 :
136 0 : for (i = 1; i <= nargs; i++) {
137 0 : sub_co = lua_tothread(L, i);
138 :
139 0 : luaL_argcheck(L, sub_co, i, "lua thread expected");
140 :
141 0 : sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx);
142 0 : if (sub_coctx == NULL) {
143 0 : return luaL_error(L, "no co ctx found");
144 : }
145 :
146 0 : if (!sub_coctx->is_uthread) {
147 0 : return luaL_error(L, "attempt to wait on a coroutine that is "
148 : "not a user thread");
149 : }
150 :
151 0 : if (sub_coctx->parent_co_ctx != coctx) {
152 0 : return luaL_error(L, "only the parent coroutine can wait on the "
153 : "thread");
154 : }
155 :
156 0 : switch (sub_coctx->co_status) {
157 0 : case NGX_HTTP_LUA_CO_ZOMBIE:
158 :
159 : ngx_http_lua_probe_info("found zombie child");
160 :
161 0 : nrets = lua_gettop(sub_coctx->co);
162 :
163 : dd("child retval count: %d, %s: %s", (int) nrets,
164 : luaL_typename(sub_coctx->co, -1),
165 : lua_tostring(sub_coctx->co, -1));
166 :
167 0 : if (nrets) {
168 0 : lua_xmove(sub_coctx->co, L, nrets);
169 : }
170 :
171 : #if 1
172 0 : ngx_http_lua_del_thread(r, L, ctx, sub_coctx);
173 0 : ctx->uthreads--;
174 : #endif
175 :
176 0 : return nrets;
177 :
178 0 : case NGX_HTTP_LUA_CO_DEAD:
179 : dd("uthread already waited: %p (parent %p)", sub_coctx,
180 : coctx);
181 :
182 0 : if (i < nargs) {
183 : /* just ignore it if it is not the last one */
184 0 : continue;
185 : }
186 :
187 : /* being the last one */
188 0 : lua_pushnil(L);
189 0 : lua_pushliteral(L, "already waited or killed");
190 0 : return 2;
191 :
192 0 : default:
193 : dd("uthread %p still alive, status: %d, parent %p", sub_coctx,
194 : sub_coctx->co_status, coctx);
195 0 : break;
196 : }
197 :
198 : ngx_http_lua_probe_user_thread_wait(L, sub_coctx->co);
199 0 : sub_coctx->waited_by_parent = 1;
200 : }
201 :
202 0 : return lua_yield(L, 0);
203 : }
204 :
205 :
206 : static int
207 0 : ngx_http_lua_uthread_kill(lua_State *L)
208 : {
209 : lua_State *sub_co;
210 : ngx_http_request_t *r;
211 : ngx_http_lua_ctx_t *ctx;
212 : ngx_http_lua_co_ctx_t *coctx, *sub_coctx;
213 :
214 0 : r = ngx_http_lua_get_req(L);
215 0 : if (r == NULL) {
216 0 : return luaL_error(L, "no request found");
217 : }
218 :
219 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
220 0 : if (ctx == NULL) {
221 0 : return luaL_error(L, "no request ctx found");
222 : }
223 :
224 0 : ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
225 : | NGX_HTTP_LUA_CONTEXT_ACCESS
226 : | NGX_HTTP_LUA_CONTEXT_CONTENT
227 : | NGX_HTTP_LUA_CONTEXT_TIMER
228 : | NGX_HTTP_LUA_CONTEXT_SSL_CERT);
229 :
230 0 : coctx = ctx->cur_co_ctx;
231 :
232 0 : sub_co = lua_tothread(L, 1);
233 0 : luaL_argcheck(L, sub_co, 1, "lua thread expected");
234 :
235 0 : sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx);
236 :
237 0 : if (sub_coctx == NULL) {
238 0 : return luaL_error(L, "no co ctx found");
239 : }
240 :
241 0 : if (!sub_coctx->is_uthread) {
242 0 : lua_pushnil(L);
243 0 : lua_pushliteral(L, "not user thread");
244 0 : return 2;
245 : }
246 :
247 0 : if (sub_coctx->parent_co_ctx != coctx) {
248 0 : lua_pushnil(L);
249 0 : lua_pushliteral(L, "killer not parent");
250 0 : return 2;
251 : }
252 :
253 0 : if (sub_coctx->pending_subreqs > 0) {
254 0 : lua_pushnil(L);
255 0 : lua_pushliteral(L, "pending subrequests");
256 0 : return 2;
257 : }
258 :
259 0 : switch (sub_coctx->co_status) {
260 0 : case NGX_HTTP_LUA_CO_ZOMBIE:
261 0 : ngx_http_lua_del_thread(r, L, ctx, sub_coctx);
262 0 : ctx->uthreads--;
263 :
264 0 : lua_pushnil(L);
265 0 : lua_pushliteral(L, "already terminated");
266 0 : return 2;
267 :
268 0 : case NGX_HTTP_LUA_CO_DEAD:
269 0 : lua_pushnil(L);
270 0 : lua_pushliteral(L, "already waited or killed");
271 0 : return 2;
272 :
273 0 : default:
274 0 : ngx_http_lua_cleanup_pending_operation(sub_coctx);
275 0 : ngx_http_lua_del_thread(r, L, ctx, sub_coctx);
276 0 : ctx->uthreads--;
277 :
278 0 : lua_pushinteger(L, 1);
279 0 : return 1;
280 : }
281 :
282 : /* not reacheable */
283 : }
284 :
285 : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|