---
src/ngx_http_lua_common.h | 11 ++-
src/ngx_http_lua_sleep.c | 179 +++++++++++++++++++++++++++++++++++++++++-----
src/ngx_http_lua_util.c | 38 ++++++++++
src/ngx_http_lua_util.h | 4 ++
4 files changed, 213 insertions(+), 19 deletions(-)
diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h
index 582815c..5e118cb 100644
--- a/src/ngx_http_lua_common.h
+++ b/src/ngx_http_lua_common.h
@@ -261,6 +261,7 @@ typedef enum {
NGX_HTTP_LUA_CO_ZOMBIE = 4, /* coroutine zombie */
} ngx_http_lua_co_status_t;
+typedef struct ngx_http_lua_wakeup_co_s ngx_http_lua_wakeup_co_t;
typedef struct ngx_http_lua_co_ctx_s ngx_http_lua_co_ctx_t;
@@ -277,6 +278,12 @@ enum {
};
+struct ngx_http_lua_wakeup_co_s {
+ ngx_http_lua_co_ctx_t *co_ctx;
+ int self_ref;
+};
+
+
struct ngx_http_lua_co_ctx_s {
void *data; /* user state for cosockets */
@@ -302,7 +309,9 @@ struct ngx_http_lua_co_ctx_s {
waited */
ngx_event_t sleep; /* used for ngx.sleep */
-
+
+ ngx_http_lua_wakeup_co_t *wakeup_co; /* wakeup userdata */
+
#ifdef NGX_LUA_USE_ASSERT
int co_top; /* stack top after yielding/creation,
only for sanity checks */
diff --git a/src/ngx_http_lua_sleep.c b/src/ngx_http_lua_sleep.c
index b59b9e7..4365e19 100644
--- a/src/ngx_http_lua_sleep.c
+++ b/src/ngx_http_lua_sleep.c
@@ -16,11 +16,59 @@
#include "ngx_http_lua_contentby.h"
+static int ngx_http_lua_ngx_co(lua_State *L);
static int ngx_http_lua_ngx_sleep(lua_State *L);
+static int ngx_http_lua_ngx_wakeup(lua_State *L);
static void ngx_http_lua_sleep_handler(ngx_event_t *ev);
static void ngx_http_lua_sleep_cleanup(void *data);
static ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r);
+static int
+ngx_http_lua_ngx_co(lua_State *L)
+{
+ int index;
+ ngx_http_request_t *r;
+ ngx_http_lua_ctx_t *ctx;
+ ngx_http_lua_co_ctx_t *coctx;
+ ngx_http_lua_wakeup_co_t *wakeup_co;
+
+ r = ngx_http_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
+ | NGX_HTTP_LUA_CONTEXT_ACCESS
+ | NGX_HTTP_LUA_CONTEXT_CONTENT
+ | NGX_HTTP_LUA_CONTEXT_TIMER);
+
+ coctx = ctx->cur_co_ctx;
+ if (coctx == NULL) {
+ return luaL_error(L, "no co ctx found");
+ }
+
+ lua_pushlightuserdata(L, &ngx_http_lua_wakeup_co_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ wakeup_co = coctx->wakeup_co;
+ if (wakeup_co == NULL) {
+ wakeup_co = (ngx_http_lua_wakeup_co_t *)
+ lua_newuserdata(L, sizeof(ngx_http_lua_wakeup_co_t));
+ if (wakeup_co == NULL) {
+ return luaL_error(L, "no memory");
+ }
+ wakeup_co->co_ctx = NULL;
+ wakeup_co->self_ref = luaL_ref(L, -2);
+ }
+ lua_rawgeti(L, -1, wakup_co->self_ref);
+ return 1;
+}
+
static int
ngx_http_lua_ngx_sleep(lua_State *L)
@@ -31,20 +79,21 @@ ngx_http_lua_ngx_sleep(lua_State *L)
ngx_http_lua_ctx_t *ctx;
ngx_http_lua_co_ctx_t *coctx;
- n = lua_gettop(L);
- if (n != 1) {
- return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n);
- }
-
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}
- delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000);
-
- if (delay < 0) {
- return luaL_error(L, "invalid sleep duration \"%d\"", delay);
+ n = lua_gettop(L);
+ if ( n == 1 ) {
+ delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000);
+ if (delay < 0) {
+ delay = -1;
+ }
+ } else if ( n == 0 ) {
+ delay = -1;
+ } else {
+ return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
@@ -66,22 +115,112 @@ ngx_http_lua_ngx_sleep(lua_State *L)
coctx->cleanup = ngx_http_lua_sleep_cleanup;
coctx->data = r;
- coctx->sleep.handler = ngx_http_lua_sleep_handler;
- coctx->sleep.data = coctx;
- coctx->sleep.log = r->connection->log;
+ if ( delay >= 0 ) {
+ coctx->sleep.handler = ngx_http_lua_sleep_handler;
+ coctx->sleep.data = coctx;
+ coctx->sleep.log = r->connection->log;
- dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay,
- (int) r->uri.len, r->uri.data);
+ dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay,
+ (int) r->uri.len, r->uri.data);
- ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);
-
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "lua ready to sleep for %d ms", delay);
+ ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "lua ready to sleep for %d ms", delay);
+ }
+ if ( coctx->wakeup_co != NULL ) {
+ coctx->wakeup_co->co_ctx = coctx;
+ }
return lua_yield(L, 0);
}
+static int
+ngx_http_lua_ngx_wakeup(lua_State *L)
+{
+ int n;
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_lua_ctx_t *ctx;
+ ngx_http_lua_co_ctx_t *coctx;
+ ngx_http_lua_wakeup_co_t *wakeup_co;
+
+ r = ngx_http_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
+ | NGX_HTTP_LUA_CONTEXT_ACCESS
+ | NGX_HTTP_LUA_CONTEXT_CONTENT
+ | NGX_HTTP_LUA_CONTEXT_TIMER);
+
+ coctx = ctx->cur_co_ctx;
+ if (coctx == NULL) {
+ return luaL_error(L, "no co ctx found");
+ }
+
+ n = lua_gettop(L);
+ if (n != 1) {
+ return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n);
+ }
+
+ wakeup_co = (ngx_http_lua_wakeup_co_t*) lua_touserdata(L, 1);
+ if (wakeup_co == NULL) {
+ return luaL_error(L, "only accept userdata returned by ngx.co");
+ }
+
+ coctx = wakeup_co->co_ctx;
+ if (coctx == NULL) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+
+ coctx->wakeup_co->co_ctx = NULL;
+
+ r = coctx->data;
+ c = r->connection;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
+
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (coctx->sleep.timer_set) {
+ ngx_del_timer(&coctx->sleep);
+ }
+
+ if (c->fd != -1) { /* not a fake connection */
+ log_ctx = c->log->data;
+ log_ctx->current_request = r;
+ }
+
+ coctx->cleanup = NULL;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "lua sleep wakeuped: \"%V?%V\"", &r->uri, &r->args);
+
+ ctx->cur_co_ctx = coctx;
+
+ if (ctx->entered_content_phase) {
+ (void) ngx_http_lua_sleep_resume(r);
+
+ } else {
+ ctx->resume_handler = ngx_http_lua_sleep_resume;
+ ngx_http_core_run_phases(r);
+ }
+
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+
void
ngx_http_lua_sleep_handler(ngx_event_t *ev)
{
@@ -93,6 +232,10 @@ ngx_http_lua_sleep_handler(ngx_event_t *ev)
coctx = ev->data;
+ if (coctx->wakeup_co != NULL) {
+ coctx->wakeup_co->co_ctx = NULL;
+ }
+
r = coctx->data;
c = r->connection;
diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c
index 65916be..576b7c2 100644
--- a/src/ngx_http_lua_util.c
+++ b/src/ngx_http_lua_util.c
@@ -72,6 +72,7 @@ char ngx_http_lua_regex_cache_key;
char ngx_http_lua_socket_pool_key;
char ngx_http_lua_coroutines_key;
char ngx_http_lua_headers_metatable_key;
+char ngx_http_lua_wakeup_co_key;
ngx_uint_t ngx_http_lua_location_hash = 0;
@@ -354,6 +355,14 @@ ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L,
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua deleting light thread");
+ if (coctx->wakeup_co != NULL) {
+ coctx->wakeup_co->co_ctx = NULL;
+ lua_pushlightuserdata(L, &ngx_http_lua_wakeup_co_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, coctx->wakeup_co->self_ref);
+ coctx->wakeup_co = NULL;
+ }
+
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
@@ -681,6 +690,11 @@ ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log)
lua_createtable(L, 0, 8 /* nrec */);
lua_rawset(L, LUA_REGISTRYINDEX);
+ /* create the registry entry for the wakeup_co userdata table */
+ lua_pushlightuserdata(L, &ngx_http_lua_wakeup_co_key);
+ lua_createtable(L, 0, 32 /* nrec */);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
#if (NGX_PCRE)
/* create the registry entry for the Lua precompiled regex object cache */
lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key);
@@ -3127,6 +3141,14 @@ ngx_http_lua_finalize_threads(ngx_http_request_t *r,
ctx->uthreads--;
}
+ if (coctx->wakeup_co != NULL) {
+ coctx->wakeup_co->co_ctx = NULL;
+ lua_pushlightuserdata(L, &ngx_http_lua_wakeup_co_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, coctx->wakeup_co->self_ref);
+ coctx->wakeup_co = NULL;
+ }
+
ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
@@ -3166,6 +3188,14 @@ ngx_http_lua_finalize_threads(ngx_http_request_t *r,
ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);
+ if (coctx->wakeup_co != NULL) {
+ coctx->wakeup_co->co_ctx = NULL;
+ lua_pushlightuserdata(L, &ngx_http_lua_wakeup_co_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, coctx->wakeup_co->self_ref);
+ coctx->wakeup_co = NULL;
+ }
+
if (!inited) {
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
lua_rawget(L, LUA_REGISTRYINDEX);
@@ -3193,6 +3223,14 @@ ngx_http_lua_finalize_threads(ngx_http_request_t *r,
if (ref != LUA_NOREF) {
ngx_http_lua_cleanup_pending_operation(coctx);
+ if (coctx->wakeup_co != NULL) {
+ coctx->wakeup_co->co_ctx = NULL;
+ lua_pushlightuserdata(L, &ngx_http_lua_wakeup_co_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, coctx->wakeup_co->self_ref);
+ coctx->wakeup_co = NULL;
+ }
+
ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);
if (!inited) {
diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h
index 42ba07e..fd9381f 100644
--- a/src/ngx_http_lua_util.h
+++ b/src/ngx_http_lua_util.h
@@ -58,7 +58,11 @@ extern char ngx_http_lua_coroutines_key;
/* key to the metatable for ngx.req.get_headers() and ngx.resp.get_headers() */
extern char ngx_http_lua_headers_metatable_key;
+/* char whose address we use as the key in Lua vm registry for
+ * wakeup_co userdata table */
+extern char ngx_http_lua_wakeup_co_key;
+extern char ngx_http_lua_socket_pool_key;
#ifndef ngx_str_set
#define ngx_str_set(str, text) \
(str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
--
1.9.5.msysgit.1
------=_Part_6832_1852803482.1440661123589
Content-Type: application/x-gtar; name=test_ngx.tgz
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=test_ngx.tgz
X-Attachment-Id: 610205f9-620b-4f35-887b-1a54bddf90f2
Content-ID: <610205f9-620b-4f35-887b-1a54bddf90f2>
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
AAA=
------=_Part_6832_1852803482.1440661123589--