From: Daniel Gruno Date: Sun, 5 Aug 2012 19:57:44 +0000 (+0000) Subject: Add a server scope for Lua states (in LuaScope), which creates a pool of states... X-Git-Tag: 2.5.0-alpha~6502 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b598e862d308df05f228fd38fffca94716ff3756;p=apache Add a server scope for Lua states (in LuaScope), which creates a pool of states with manageable minimum and maximum size. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1369656 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/lua/lua_vmprep.c b/modules/lua/lua_vmprep.c index 5b436de9be..14ae97bd6d 100644 --- a/modules/lua/lua_vmprep.c +++ b/modules/lua/lua_vmprep.c @@ -23,6 +23,15 @@ APLOG_USE_MODULE(lua); +#if APR_HAS_THREADS + apr_thread_mutex_t *ap_lua_mutex; + +void ap_lua_init_mutex(apr_pool_t *pool, server_rec *s) +{ + apr_thread_mutex_create(&ap_lua_mutex, APR_THREAD_MUTEX_DEFAULT, pool); +} +#endif + /* forward dec'l from this file */ #if 0 @@ -127,7 +136,7 @@ AP_LUA_DECLARE(void) ap_lua_load_apache2_lmodule(lua_State *L) makeintegerfield(L, AUTHZ_NEUTRAL); makeintegerfield(L, AUTHZ_GENERAL_ERROR); makeintegerfield(L, AUTHZ_DENIED_NO_USER); - + /* makeintegerfield(L, HTTP_CONTINUE); makeintegerfield(L, HTTP_SWITCHING_PROTOCOLS); @@ -201,6 +210,16 @@ static apr_status_t cleanup_lua(void *l) return APR_SUCCESS; } +static apr_status_t server_cleanup_lua(void *resource) +{ + ap_lua_server_spec* spec = (ap_lua_server_spec*) resource; + AP_DEBUG_ASSERT(spec != NULL); + if (spec->L != NULL) { + lua_close((lua_State *) spec->L); + } + return APR_SUCCESS; +} + /* munge_path(L, "path", @@ -333,6 +352,39 @@ static apr_status_t vm_construct(lua_State **vm, void *params, apr_pool_t *lifec return APR_SUCCESS; } +ap_lua_vm_spec* copy_vm_spec(apr_pool_t* pool, ap_lua_vm_spec* spec) +{ + ap_lua_vm_spec* copied_spec = apr_pcalloc(pool, sizeof(ap_lua_vm_spec)); + copied_spec->bytecode_len = spec->bytecode_len; + copied_spec->bytecode = spec->bytecode ? apr_pstrdup(pool, spec->bytecode) : 0; + copied_spec->cb = spec->cb; + copied_spec->cb_arg = NULL; + copied_spec->file = spec->file ? apr_pstrdup(pool, spec->file) : 0; + copied_spec->package_cpaths = apr_array_copy(pool, spec->package_cpaths); + copied_spec->package_paths = apr_array_copy(pool, spec->package_paths); + copied_spec->pool = pool; + copied_spec->scope = AP_LUA_SCOPE_SERVER; + copied_spec->codecache = spec->codecache; + return copied_spec; +} + +static apr_status_t server_vm_construct(lua_State **resource, void *params, apr_pool_t *pool) +{ + lua_State* L; + ap_lua_server_spec* spec = apr_pcalloc(pool, sizeof(ap_lua_server_spec)); + if (vm_construct(&L, params, pool) == APR_SUCCESS) { + spec->finfo = apr_pcalloc(pool, sizeof(ap_lua_finfo)); + if (L != NULL) { + spec->L = L; + *resource = (void*) spec; + lua_pushlightuserdata(L, spec); + lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec"); + return APR_SUCCESS; + } + } + return APR_EGENERAL; +} + /** * Function used to create a lua_State instance bound into the web * server in the appropriate scope. @@ -341,57 +393,96 @@ AP_LUA_DECLARE(lua_State*)ap_lua_get_lua_state(apr_pool_t *lifecycle_pool, ap_lua_vm_spec *spec, request_rec* r) { lua_State *L = NULL; + ap_lua_finfo *cache_info; int tryCache = 0; - if (apr_pool_userdata_get((void **)&L, spec->file, - lifecycle_pool) == APR_SUCCESS) { - - if(L==NULL) { + + if (spec->scope == AP_LUA_SCOPE_SERVER) { + char *hash; + apr_reslist_t* reslist = NULL; + ap_lua_server_spec* sspec = NULL; + hash = apr_psprintf(r->pool, "reslist:%s", spec->file); +#if APR_HAS_THREADS + apr_thread_mutex_lock(ap_lua_mutex); +#endif + if (apr_pool_userdata_get((void **)&reslist, hash, + r->server->process->pool) == APR_SUCCESS) { + if (reslist != NULL) { + if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) { + L = sspec->L; + cache_info = sspec->finfo; + } + } + } + if (L == NULL) { + ap_lua_vm_spec* server_spec = copy_vm_spec(r->server->process->pool, spec); + apr_reslist_create(&reslist, spec->vm_min, spec->vm_max, spec->vm_max, 0, + (apr_reslist_constructor) server_vm_construct, + (apr_reslist_destructor) server_cleanup_lua, + server_spec, r->server->process->pool); + apr_pool_userdata_set(reslist, hash, NULL, + r->server->process->pool); + if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) { + L = sspec->L; + cache_info = sspec->finfo; + } + } +#if APR_HAS_THREADS + apr_thread_mutex_unlock(ap_lua_mutex); +#endif + } + else { + if (apr_pool_userdata_get((void **)&L, spec->file, + lifecycle_pool) != APR_SUCCESS) { + L = NULL; + } + } + if(L==NULL) { ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01483) - "creating lua_State with file %s", spec->file); + "creating lua_State with file %s", spec->file); /* not available, so create */ - + if(!vm_construct(&L, spec, lifecycle_pool)) { - AP_DEBUG_ASSERT(L != NULL); - apr_pool_userdata_set(L, + AP_DEBUG_ASSERT(L != NULL); + apr_pool_userdata_set(L, spec->file, cleanup_lua, lifecycle_pool); } - } } - /*}*/ + if (spec->codecache == AP_LUA_CACHE_FOREVER || (spec->bytecode && spec->bytecode_len > 0)) { tryCache = 1; } else { - ap_lua_finfo *cache_info; - char* mkey = apr_psprintf(r->pool, "ap_lua_modified:%s", spec->file); - if (apr_pool_userdata_get((void **)&cache_info, mkey, - lifecycle_pool) == APR_SUCCESS) { + char* mkey; + if (spec->scope != AP_LUA_SCOPE_SERVER) { + mkey = apr_psprintf(r->pool, "ap_lua_modified:%s", spec->file); + apr_pool_userdata_get((void **)&cache_info, mkey, lifecycle_pool); if (cache_info == NULL) { cache_info = apr_pcalloc(lifecycle_pool, sizeof(ap_lua_finfo)); } - if (spec->codecache == AP_LUA_CACHE_STAT) { - apr_finfo_t lua_finfo; - apr_stat(&lua_finfo, spec->file, APR_FINFO_MTIME|APR_FINFO_SIZE, lifecycle_pool); - - /* On first visit, modified will be zero, but that's fine - The file is - loaded in the vm_construct function. - */ - if ((cache_info->modified == lua_finfo.mtime && cache_info->size == lua_finfo.size) \ - || cache_info->modified == 0) tryCache = 1; - cache_info->modified = lua_finfo.mtime; - cache_info->size = lua_finfo.size; - } - else if (spec->codecache == AP_LUA_CACHE_NEVER) { - if (cache_info->runs == 0) tryCache = 1; + } + if (spec->codecache == AP_LUA_CACHE_STAT) { + apr_finfo_t lua_finfo; + apr_stat(&lua_finfo, spec->file, APR_FINFO_MTIME|APR_FINFO_SIZE, lifecycle_pool); + + /* On first visit, modified will be zero, but that's fine - The file is + loaded in the vm_construct function. + */ + if ((cache_info->modified == lua_finfo.mtime && cache_info->size == lua_finfo.size) \ + || cache_info->modified == 0) { + tryCache = 1; } - cache_info->runs++; + cache_info->modified = lua_finfo.mtime; + cache_info->size = lua_finfo.size; + } + else if (spec->codecache == AP_LUA_CACHE_NEVER) { + if (cache_info->runs == 0) tryCache = 1; } - else { - tryCache = 1; + cache_info->runs++; + if (spec->scope != AP_LUA_SCOPE_SERVER) { + apr_pool_userdata_set((void*) cache_info, mkey, NULL, lifecycle_pool); } - apr_pool_userdata_set((void*) cache_info, mkey, NULL, lifecycle_pool); } if (tryCache == 0 && spec->scope != AP_LUA_SCOPE_ONCE) { int rc; diff --git a/modules/lua/lua_vmprep.h b/modules/lua/lua_vmprep.h index 29db19a7a1..e9d101f7db 100644 --- a/modules/lua/lua_vmprep.h +++ b/modules/lua/lua_vmprep.h @@ -29,6 +29,7 @@ #include "apr_file_info.h" #include "apr_time.h" #include "apr_pools.h" +#include "apr_reslist.h" #ifndef VMPREP_H @@ -39,16 +40,15 @@ #define AP_LUA_SCOPE_REQUEST 2 #define AP_LUA_SCOPE_CONN 3 #define AP_LUA_SCOPE_THREAD 4 +#define AP_LUA_SCOPE_SERVER 5 #define AP_LUA_CACHE_UNSET 0 #define AP_LUA_CACHE_NEVER 1 #define AP_LUA_CACHE_STAT 2 #define AP_LUA_CACHE_FOREVER 3 - typedef void (*ap_lua_state_open_callback) (lua_State *L, apr_pool_t *p, void *ctx); - /** * Specification for a lua virtual machine */ @@ -61,8 +61,10 @@ typedef struct /* name of base file to load in the vm */ const char *file; - /* APL_SCOPE_ONCE | APL_SCOPE_REQUEST | APL_SCOPE_CONN | APL_SCOPE_THREAD */ + /* APL_SCOPE_ONCE | APL_SCOPE_REQUEST | APL_SCOPE_CONN | APL_SCOPE_THREAD | APL_SCOPE_SERVER */ int scope; + unsigned int vm_min; + unsigned int vm_max; ap_lua_state_open_callback cb; void* cb_arg; @@ -97,6 +99,11 @@ typedef struct { apr_size_t size; } ap_lua_finfo; +typedef struct { + lua_State* L; + ap_lua_finfo* finfo; +} ap_lua_server_spec; + /* remove and make static once out of mod_wombat.c */ AP_LUA_DECLARE(void) ap_lua_openlibs(lua_State *L); diff --git a/modules/lua/mod_lua.c b/modules/lua/mod_lua.c index 8baf7d8edd..f72c1dd0aa 100644 --- a/modules/lua/mod_lua.c +++ b/modules/lua/mod_lua.c @@ -65,7 +65,6 @@ static void report_lua_error(lua_State *L, request_rec *r) const char *lua_response; r->status = HTTP_INTERNAL_SERVER_ERROR; r->content_type = "text/html"; - ap_rputs("Error!\n", r); ap_rputs("

", r); lua_response = lua_tostring(L, -1); @@ -109,6 +108,29 @@ static const char *scope_to_string(unsigned int scope) } } +static void ap_lua_release_state(lua_State* L, ap_lua_vm_spec* spec, request_rec* r) { + char *hash; + apr_reslist_t* reslist = NULL; + if (spec->scope == AP_LUA_SCOPE_SERVER) { + ap_lua_server_spec* sspec = NULL; + lua_settop(L, 0); + lua_getfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec"); + sspec = (ap_lua_server_spec*) lua_touserdata(L, 1); + hash = apr_psprintf(r->pool, "reslist:%s", spec->file); + if (apr_pool_userdata_get((void **)&reslist, hash, + r->server->process->pool) == APR_SUCCESS) { + AP_DEBUG_ASSERT(sspec != NULL); + if (reslist != NULL) { + apr_reslist_release(reslist, sspec); + } + } + } +} + +#if APR_HAS_THREADS +extern void ap_lua_init_mutex(apr_pool_t *pool, server_rec *s); +#endif + static ap_lua_vm_spec *create_vm_spec(apr_pool_t **lifecycle_pool, request_rec *r, const ap_lua_dir_cfg *cfg, @@ -131,7 +153,9 @@ static ap_lua_vm_spec *create_vm_spec(apr_pool_t **lifecycle_pool, spec->bytecode = bytecode; spec->bytecode_len = bytecode_len; spec->codecache = (cfg->codecache == AP_LUA_CACHE_UNSET) ? AP_LUA_CACHE_STAT : cfg->codecache; - + spec->vm_min = cfg->vm_min ? cfg->vm_min : 1; + spec->vm_max = cfg->vm_max ? cfg->vm_max : 1; + if (filename) { char *file; apr_filepath_merge(&file, server_cfg->root_path, @@ -161,6 +185,9 @@ static ap_lua_vm_spec *create_vm_spec(apr_pool_t **lifecycle_pool, case AP_LUA_SCOPE_THREAD: pool = apr_thread_pool_get(r->connection->current_thread); break; + case AP_LUA_SCOPE_SERVER: + pool = r->server->process->pool; + break; #endif default: ap_assert(0); @@ -230,6 +257,7 @@ static int lua_handler(request_rec *r) /* TODO annotate spec with failure reason */ r->status = HTTP_INTERNAL_SERVER_ERROR; ap_rputs("Unable to compile VM, see logs", r); + ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR; } ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, APLOGNO(01474) "got a vm!"); @@ -239,12 +267,14 @@ static int lua_handler(request_rec *r) "lua: Unable to find function %s in %s", "handle", spec->file); + ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR; } ap_lua_run_lua_request(L, r); if (lua_pcall(L, 1, 0, 0)) { report_lua_error(L, r); } + ap_lua_release_state(L, spec, r); } return OK; } @@ -300,6 +330,7 @@ static int lua_request_rec_hook_harness(request_rec *r, const char *name, int ap "lua: Unable to find function %s in %s", hook_spec->function_name, hook_spec->file_name); + ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR; } @@ -316,6 +347,7 @@ static int lua_request_rec_hook_harness(request_rec *r, const char *name, int ap if (lua_pcall(L, 1, 1, 0)) { report_lua_error(L, r); + ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR; } rc = DECLINED; @@ -323,6 +355,7 @@ static int lua_request_rec_hook_harness(request_rec *r, const char *name, int ap rc = lua_tointeger(L, -1); } if (rc != DECLINED) { + ap_lua_release_state(L, spec, r); return rc; } } @@ -373,6 +406,7 @@ static int lua_map_handler(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01477) "lua: Failed to obtain lua interpreter for %s %s", function_name, filename); + ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR; } @@ -383,6 +417,7 @@ static int lua_map_handler(request_rec *r) "lua: Unable to find function %s in %s", function_name, filename); + ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR; } @@ -399,6 +434,7 @@ static int lua_map_handler(request_rec *r) if (lua_pcall(L, 1, 1, 0)) { report_lua_error(L, r); + ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR; } rc = DECLINED; @@ -406,6 +442,7 @@ static int lua_map_handler(request_rec *r) rc = lua_tointeger(L, -1); } if (rc != DECLINED) { + ap_lua_release_state(L, spec, r); return rc; } } @@ -1110,12 +1147,33 @@ static const char *register_lua_scope(cmd_parms *cmd, #endif cfg->vm_scope = AP_LUA_SCOPE_THREAD; } + else if (strcmp("server", scope) == 0) { + unsigned int vmin, vmax; +#if !APR_HAS_THREADS + return apr_psprintf(cmd->pool, + "Scope type of '%s' cannot be used because this " + "server does not have threading support " + "(APR_HAS_THREADS)" + scope); +#endif + cfg->vm_scope = AP_LUA_SCOPE_SERVER; + vmin = min ? atoi(min) : 1; + vmax = max ? atoi(max) : 1; + if (vmin == 0) { + vmin = 1; + } + if (vmax < vmin) { + vmax = vmin; + } + cfg->vm_min = vmin; + cfg->vm_max = vmax; + } else { return apr_psprintf(cmd->pool, "Invalid value for LuaScope, '%s', acceptable " - "values are: 'once', 'request', 'conn', 'server'" + "values are: 'once', 'request', 'conn'" #if APR_HAS_THREADS - ", 'thread'" + ", 'thread', 'server'" #endif ,scope); } @@ -1203,6 +1261,7 @@ static authz_status lua_authz_check(request_rec *r, const char *require_line, ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02319) "Unable to find function %s in %s", prov_spec->function_name, prov_spec->file_name); + ap_lua_release_state(L, spec, r); return AUTHZ_GENERAL_ERROR; } ap_lua_run_lua_request(L, r); @@ -1211,6 +1270,7 @@ static authz_status lua_authz_check(request_rec *r, const char *require_line, if (!lua_checkstack(L, prov_spec->args->nelts)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02315) "Error: authz provider %s: too many arguments", prov_spec->name); + ap_lua_release_state(L, spec, r); return AUTHZ_GENERAL_ERROR; } for (i = 0; i < prov_spec->args->nelts; i++) { @@ -1223,14 +1283,17 @@ static authz_status lua_authz_check(request_rec *r, const char *require_line, const char *err = lua_tostring(L, -1); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02316) "Error executing authz provider %s: %s", prov_spec->name, err); + ap_lua_release_state(L, spec, r); return AUTHZ_GENERAL_ERROR; } if (!lua_isnumber(L, -1)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02317) "Error: authz provider %s did not return integer", prov_spec->name); + ap_lua_release_state(L, spec, r); return AUTHZ_GENERAL_ERROR; } result = lua_tointeger(L, -1); + ap_lua_release_state(L, spec, r); switch (result) { case AUTHZ_DENIED: case AUTHZ_GRANTED: @@ -1389,6 +1452,8 @@ static void *create_dir_config(apr_pool_t *p, char *dir) cfg->dir = apr_pstrdup(p, dir); cfg->vm_scope = AP_LUA_SCOPE_UNSET; cfg->codecache = AP_LUA_CACHE_UNSET; + cfg->vm_min = 0; + cfg->vm_max = 0; return cfg; } @@ -1452,6 +1517,9 @@ static void *merge_dir_config(apr_pool_t *p, void *basev, void *overridesv) a->vm_scope = (overrides->vm_scope == AP_LUA_SCOPE_UNSET) ? base->vm_scope: overrides->vm_scope; a->inherit = (overrides->inherit== AP_LUA_INHERIT_UNSET) ? base->inherit : overrides->inherit; a->codecache = (overrides->codecache== AP_LUA_CACHE_UNSET) ? base->codecache : overrides->codecache; + + a->vm_min = (overrides->vm_min== 0) ? base->vm_min : overrides->vm_min; + a->vm_max = (overrides->vm_max== 0) ? base->vm_max : overrides->vm_max; if (a->inherit == AP_LUA_INHERIT_UNSET || a->inherit == AP_LUA_INHERIT_PARENT_FIRST) { a->package_paths = apr_array_append(p, base->package_paths, overrides->package_paths); @@ -1529,6 +1597,9 @@ static void lua_register_hooks(apr_pool_t *p) APR_OPTIONAL_HOOK(ap_lua, lua_request, lua_request_hook, NULL, NULL, APR_HOOK_REALLY_FIRST); ap_hook_handler(lua_map_handler, NULL, NULL, AP_LUA_HOOK_FIRST); +#if APR_HAS_THREADS + ap_hook_child_init(ap_lua_init_mutex, NULL, NULL, APR_HOOK_MIDDLE); +#endif /* providers */ lua_authz_providers = apr_hash_make(p); } diff --git a/modules/lua/mod_lua.h b/modules/lua/mod_lua.h index b737c0b672..3e06d1fbec 100644 --- a/modules/lua/mod_lua.h +++ b/modules/lua/mod_lua.h @@ -39,6 +39,7 @@ #include "apr_file_info.h" #include "apr_time.h" #include "apr_hooks.h" +#include "apr_reslist.h" /* Allow for Lua 5.2 backwards compatibility */ #define LUA_COMPAT_ALL @@ -111,6 +112,8 @@ typedef struct * AP_LUA_SCOPE_ONCE | AP_LUA_SCOPE_REQUEST | AP_LUA_SCOPE_CONN | AP_LUA_SCOPE_SERVER */ unsigned int vm_scope; + unsigned int vm_min; + unsigned int vm_max; /* info for the hook harnesses */ apr_hash_t *hooks; /* */