Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

TS-1634: reimplement Lua state management to support reload

  • Loading branch information...
commit 02414852f57907469d4cced9bf93f4d95900261f 1 parent 07ff399
@jpeach jpeach authored
View
2  CHANGES
@@ -2,6 +2,8 @@
Changes with Apache Traffic Server 3.3.1
+ *) [TS-1634] reimplement Lua state management to support reload
+
*) [TS-1716] authproxy fails to reserve an argument index in global mode
*) [TS-1710] esi plugin enhancement such as support forward proxy
View
2  plugins/experimental/lua/Makefile.am
@@ -30,7 +30,7 @@ lua_la_CPPFLAGS = \
pkglib_LTLIBRARIES = lua.la
lua_la_LIBADD = ${LUA_LIBS}
-lua_la_SOURCES = remap.cc plugin.cc lapi.cc lutil.cc lconfig.cc hook.cc
+lua_la_SOURCES = remap.cc plugin.cc lapi.cc lutil.cc lconfig.cc hook.cc state.cc
lua_la_LDFLAGS = -module -avoid-version -shared
endif
View
299 plugins/experimental/lua/hook.cc
@@ -22,21 +22,11 @@
#include "lapi.h"
#include "lutil.h"
#include "hook.h"
+#include "state.h"
#include <memory> // placement new
#include <ink_config.h>
-typedef TSCont HookDemuxTable[TS_HTTP_LAST_HOOK];
-
-// Continuation tables for global, txn and ssn hooks. These are all indexed by the TSHttpHookID and
-// are used to select which callback to invoke during event demuxing.
-static struct
-{
- HookDemuxTable global;
- HookDemuxTable txn;
- HookDemuxTable ssn;
-} HttpHookDemuxTable;
-
const char *
HttpHookName(TSHttpHookID hookid)
{
@@ -104,31 +94,74 @@ LuaPushEventData(lua_State * lua, TSEvent event, void * edata)
}
}
-// The per-ssn and per-txn argument mechanism stores a pointer, so it's NULL when not set. Unfortunately, 0 is a
-// legitimate Lua reference value (all valies except LUA_NOREF are legitimate), so we can't distinguish NULL from a 0
-// reference. In 64-bit mode we have some extra bits and we can maintain the state, but in 32-bit mode, we need to
-// allocate the LuaHookReference to have enough space to store the state.
-
-union LuaHookReference
-{
- struct ref {
- bool set;
- int value;
- } ref;
- void * storage;
-};
// For 64-bit pointers, we can inline the LuaHookReference, otherwise we need an extra malloc.
+//
#if SIZEOF_VOID_POINTER >= 8
#define INLINE_LUA_HOOK_REFERENCE 1
#else
#undef INLINE_LUA_HOOK_REFERENCE
#endif
-// Verify that LuaHookReference fits in sizeof(void *).
-#if INLINE_LUA_HOOK_REFERENCE
-extern char __LuaHookReferenceSizeCheck[sizeof(LuaHookReference) == SIZEOF_VOID_POINTER ? 0 : -1];
+template <typename t1, typename t2>
+struct inline_tuple
+{
+ typedef t1 first_type;
+ typedef t2 second_type;
+ typedef inline_tuple<first_type, second_type> this_type;
+
+ union {
+ struct {
+ first_type first;
+ second_type second;
+ } s;
+ void * ptr;
+ } storage;
+
+ first_type& first() { return storage.s.first; }
+ second_type& second() { return storage.s.second; }
+
+ static void * allocate(const first_type first, const second_type second) {
+#if defined(INLINE_LUA_HOOK_REFERENCE)
+ typedef char __size_check[sizeof(this_type) == sizeof(void *) ? 0 : -1];
+
+ this_type obj;
+ obj.first() = first;
+ obj.second() = second;
+ return obj.storage.ptr;
+#else
+ this_type * ptr = (this_type *)TSmalloc(sizeof(this_type));
+ ptr->first() = first;
+ ptr->second() = second;
+ return ptr;
+#endif
+ }
+
+ static void free(void * ptr) {
+#if defined(INLINE_LUA_HOOK_REFERENCE)
+ // Nothing to do, because we never allocated.
+#else
+ TSfree(ptr);
+#endif
+ }
+
+ static this_type get(void * ptr) {
+#if defined(INLINE_LUA_HOOK_REFERENCE)
+ this_type obj;
+ obj.storage.ptr = ptr;
+ return obj;
+#else
+ return ptr ? *(this_type *)ptr : this_type();
#endif
+ }
+
+};
+
+// The per-ssn and per-txn argument mechanism stores a pointer, so it's NULL when not set. Unfortunately, 0 is a
+// legitimate Lua reference value (all values except LUA_NOREF are legitimate), so we can't distinguish NULL from a 0
+// reference. In 64-bit mode we have some extra bits and we can maintain the state, but in 32-bit mode, we need to
+// allocate the LuaHookReference to have enough space to store the state.
+typedef inline_tuple<int, bool> LuaHookReference;
static void *
LuaHttpObjectArgGet(TSHttpSsn ssn)
@@ -157,48 +190,22 @@ LuaHttpObjectArgSet(TSHttpTxn txn, void * ptr)
template<typename T> static int
LuaGetArgReference(T ptr)
{
- LuaHookReference href;
-
- href.storage = LuaHttpObjectArgGet(ptr);
-
-#if !defined(INLINE_LUA_HOOK_REFERENCE)
- if (href.storage) {
- href = *(LuaHookReference *)href.storage;
- }
-#endif
-
- return (href.ref.set) ? href.ref.value : LUA_NOREF;
+ LuaHookReference href(LuaHookReference::get(LuaHttpObjectArgGet(ptr)));
+ // Only return the Lua ref if it was previously set.
+ return href.second() ? href.first() : LUA_NOREF;
}
template <typename T> void
LuaSetArgReference(T ptr, int ref)
{
- LuaHookReference href;
-
- href.storage = NULL;
- href.ref.value = ref;
- href.ref.set = true;
-
-#if defined(INLINE_LUA_HOOK_REFERENCE)
- LuaHttpObjectArgSet(ptr, href.storage);
-#else
- LuaHookReference * tmp = (LuaHookReference *)LuaHttpObjectArgGet(ptr);
- if (tmp) {
- *tmp = href;
- } else {
- tmp = (LuaHookReference *)TSmalloc(sizeof(LuaHookReference));
- *tmp = href;
- LuaHttpObjectArgSet(ptr, tmp);
- }
-#endif
+ LuaHookReference::free(LuaHttpObjectArgGet(ptr));
+ LuaHttpObjectArgSet(ptr, LuaHookReference::allocate(ref, true));
}
template <typename T> static void
LuaClearArgReference(T ptr)
{
-#if !defined(INLINE_LUA_HOOK_REFERENCE)
- TSfree(LuaHttpObjectArgGet(ptr));
-#endif
+ LuaHookReference::free(LuaHttpObjectArgGet(ptr));
LuaHttpObjectArgSet(ptr, NULL);
}
@@ -206,31 +213,6 @@ LuaClearArgReference(T ptr)
template void LuaSetArgReference<TSHttpSsn>(TSHttpSsn ssn, int ref);
template void LuaSetArgReference<TSHttpTxn>(TSHttpTxn txn, int ref);
-static LuaThreadInstance *
-LuaDemuxThreadInstance()
-{
- LuaThreadInstance * lthread;
-
- lthread = LuaGetThreadInstance();
-
- if (lthread == NULL) {
- lthread = tsnew<LuaThreadInstance>();
- lthread->lua = LuaPluginNewState();
- LuaSetThreadInstance(lthread);
- LuaPluginLoad(lthread->lua, LuaPlugin);
- }
-
- return lthread;
-}
-
-static TSHttpHookID
-LuaDemuxHookID(TSCont cont)
-{
- TSHttpHookID hookid = (TSHttpHookID)(intptr_t)TSContDataGet(cont);
- TSAssert(HookIsValid(hookid));
- return hookid;
-}
-
static void
LuaDemuxInvokeCallback(lua_State * lua, TSHttpHookID hookid, TSEvent event, void * edata, int ref)
{
@@ -262,9 +244,9 @@ LuaDemuxInvokeCallback(lua_State * lua, TSHttpHookID hookid, TSEvent event, void
TSReleaseAssert(0);
}
- // The item on the top of the stack *ought* to be the callback function. However when we register a cleanup function
- // to release the callback reference (because the ssn ot txn closes), then we won't have a function because there's
- // nothing to do here.
+ // The item on the top of the stack *ought* to be the callback function. However when we register a
+ // cleanup function to release the callback reference (because the ssn or txn closes), then we won't
+ // have a function because there's nothing to do here.
if (!lua_isnil(lua, -1)) {
TSAssert(lua_isfunction(lua, -1));
@@ -282,61 +264,63 @@ LuaDemuxInvokeCallback(lua_State * lua, TSHttpHookID hookid, TSEvent event, void
lua_pop(lua, lua_gettop(lua) - nitems);
}
-static int
-LuaDemuxGlobalHook(TSCont cont, TSEvent event, void * edata)
+int
+LuaDemuxGlobalHook(TSHttpHookID hookid, TSCont cont, TSEvent event, void * edata)
{
- TSHttpHookID hookid = LuaDemuxHookID(cont);
- LuaThreadInstance * lthread = LuaDemuxThreadInstance();
- int ref = lthread->hooks[hookid];
+ instanceid_t instanceid = (uintptr_t)TSContDataGet(cont);
+ ScopedLuaState lstate(instanceid);
+ int ref = lstate->hookrefs[hookid];
- LuaLogDebug("%s lthread=%p event=%d edata=%p, ref=%d",
- HttpHookName(hookid), lthread, event, edata, ref);
+ LuaLogDebug("%u/%p %s event=%d edata=%p, ref=%d",
+ instanceid, lstate->lua,
+ HttpHookName(hookid), event, edata, ref);
if (ref == LUA_NOREF) {
LuaLogError("no Lua callback for hook %s", HttpHookName(hookid));
return TS_EVENT_ERROR;
}
- LuaDemuxInvokeCallback(lthread->lua, hookid, event, edata, ref);
+ LuaDemuxInvokeCallback(lstate->lua, hookid, event, edata, ref);
return TS_EVENT_NONE;
}
-static int
-LuaDemuxTxnHook(TSCont cont, TSEvent event, void * edata)
+int
+LuaDemuxTxnHook(TSHttpHookID hookid, TSCont cont, TSEvent event, void * edata)
{
- TSHttpHookID hookid = LuaDemuxHookID(cont);
- LuaThreadInstance * lthread = LuaDemuxThreadInstance();
int ref = LuaGetArgReference((TSHttpTxn)edata);
+ instanceid_t instanceid = (uintptr_t)TSContDataGet(cont);
+ ScopedLuaState lstate(instanceid);
- LuaLogDebug("%s(%s) lthread=%p event=%d edata=%p",
- __func__, HttpHookName(hookid), lthread, event, edata);
+ LuaLogDebug("%s(%s) instanceid=%u event=%d edata=%p",
+ __func__, HttpHookName(hookid), instanceid, event, edata);
if (ref == LUA_NOREF) {
LuaLogError("no Lua callback for hook %s", HttpHookName(hookid));
return TS_EVENT_ERROR;
}
- LuaDemuxInvokeCallback(lthread->lua, hookid, event, edata, ref);
+ LuaDemuxInvokeCallback(lstate->lua, hookid, event, edata, ref);
if (event == TS_EVENT_HTTP_TXN_CLOSE) {
LuaLogDebug("unref event handler %d", ref);
- luaL_unref(lthread->lua, LUA_REGISTRYINDEX, ref);
+ luaL_unref(lstate->lua, LUA_REGISTRYINDEX, ref);
LuaClearArgReference((TSHttpTxn)edata);
}
return TS_EVENT_NONE;
}
-static int
-LuaDemuxSsnHook(TSCont cont, TSEvent event, void * edata)
+int
+LuaDemuxSsnHook(TSHttpHookID hookid, TSCont cont, TSEvent event, void * edata)
{
- TSHttpHookID hookid = LuaDemuxHookID(cont);
- LuaThreadInstance * lthread = LuaDemuxThreadInstance();
+ instanceid_t instanceid = (uintptr_t)TSContDataGet(cont);
+ ScopedLuaState lstate(instanceid);
TSHttpSsn ssn;
int ref;
- // The edata might be a Txn or a Ssn, depending on the event type. If we get here, it's because we registered a
- // callback on the Ssn, so we need to get back to the Ssn object in order to the the callback table reference ...
+ // The edata might be a Txn or a Ssn, depending on the event type. If we get here, it's because we
+ // registered a callback on the Ssn, so we need to get back to the Ssn object in order to the the
+ // callback table reference ...
switch (event) {
case TS_EVENT_HTTP_SSN_START:
case TS_EVENT_HTTP_SSN_CLOSE:
@@ -346,8 +330,8 @@ LuaDemuxSsnHook(TSCont cont, TSEvent event, void * edata)
ssn = TSHttpTxnSsnGet((TSHttpTxn)edata);
}
- LuaLogDebug("%s(%s) lthread=%p event=%d edata=%p",
- __func__, HttpHookName(hookid), lthread, event, edata);
+ LuaLogDebug("%s(%s) instanceid=%u event=%d edata=%p",
+ __func__, HttpHookName(hookid), instanceid, event, edata);
ref = LuaGetArgReference(ssn);
if (ref == LUA_NOREF) {
@@ -355,11 +339,11 @@ LuaDemuxSsnHook(TSCont cont, TSEvent event, void * edata)
return TS_EVENT_ERROR;
}
- LuaDemuxInvokeCallback(lthread->lua, hookid, event, edata, ref);
+ LuaDemuxInvokeCallback(lstate->lua, hookid, event, edata, ref);
if (event == TS_EVENT_HTTP_SSN_CLOSE) {
LuaLogDebug("unref event handler %d", ref);
- luaL_unref(lthread->lua, LUA_REGISTRYINDEX, ref);
+ luaL_unref(lstate->lua, LUA_REGISTRYINDEX, ref);
LuaClearArgReference((TSHttpSsn)edata);
}
@@ -369,8 +353,8 @@ LuaDemuxSsnHook(TSCont cont, TSEvent event, void * edata)
bool
LuaRegisterHttpHooks(lua_State * lua, void * obj, LuaHookAddFunction add, int hooks)
{
- bool hooked_close = false;
- const TSHttpHookID closehook = (add == LuaHttpSsnHookAdd ? TS_HTTP_SSN_CLOSE_HOOK : TS_HTTP_TXN_CLOSE_HOOK);
+ bool hooked_close = false;
+ const TSHttpHookID closehook = (add == LuaHttpSsnHookAdd ? TS_HTTP_SSN_CLOSE_HOOK : TS_HTTP_TXN_CLOSE_HOOK);
TSAssert(add == LuaHttpSsnHookAdd || add == LuaHttpTxnHookAdd);
@@ -380,6 +364,9 @@ LuaRegisterHttpHooks(lua_State * lua, void * obj, LuaHookAddFunction add, int ho
// The value on the top of the stack (index -1) MUST be the callback table.
TSAssert(lua_istable(lua, lua_gettop(lua)));
+ // Now we need our LuaThreadState to access the hook tables.
+ ScopedLuaState lstate(lua);
+
// Walk the table and register the hook for each entry.
lua_pushnil(lua); // Push the first key, makes the callback table index -2.
while (lua_next(lua, -2) != 0) {
@@ -388,8 +375,8 @@ LuaRegisterHttpHooks(lua_State * lua, void * obj, LuaHookAddFunction add, int ho
// uses 'key' (at index -2) and 'value' (at index -1).
// LuaLogDebug("key=%s value=%s\n", ltypeof(lua, -2), ltypeof(lua, -1));
- // Now the key (index -2) and value (index -1) got pushed onto the stack. The key must be a hook ID and the value
- // must be a callback function.
+ // Now the key (index -2) and value (index -1) got pushed onto the stack. The key must be a hook ID and
+ // the value must be a callback function.
luaL_checktype(lua, -1, LUA_TFUNCTION);
hookid = (TSHttpHookID)luaL_checkint(lua, -2);
@@ -403,7 +390,7 @@ LuaRegisterHttpHooks(lua_State * lua, void * obj, LuaHookAddFunction add, int ho
}
// At demux time, we need the hook ID and the table (or function) ref.
- add(obj, hookid);
+ add(obj, lstate.instance(), hookid);
LuaLogDebug("registered callback table %d for event %s on object %p",
hooks, HttpHookName(hookid), obj);
@@ -412,34 +399,32 @@ LuaRegisterHttpHooks(lua_State * lua, void * obj, LuaHookAddFunction add, int ho
lua_pop(lua, 1);
}
- // we always need to hook the close because we keep a reference to the callback table and we need to release that
- // reference when the object's lifetime ends.
+ // we always need to hook the close because we keep a reference to the callback table and we need to
+ // release that reference when the object's lifetime ends.
if (!hooked_close) {
- add(obj, closehook);
+ add(obj, lstate.instance(), closehook);
}
return true;
}
void
-LuaHttpSsnHookAdd(void * ssn, TSHttpHookID hookid)
+LuaHttpSsnHookAdd(void * ssn, const LuaPluginInstance * instance, TSHttpHookID hookid)
{
- TSHttpSsnHookAdd((TSHttpSsn)ssn, hookid, HttpHookDemuxTable.ssn[hookid]);
+ TSHttpSsnHookAdd((TSHttpSsn)ssn, hookid, instance->demux.ssn[hookid]);
}
void
-LuaHttpTxnHookAdd(void * txn, TSHttpHookID hookid)
+LuaHttpTxnHookAdd(void * txn, const LuaPluginInstance * instance, TSHttpHookID hookid)
{
- TSHttpTxnHookAdd((TSHttpTxn)txn, hookid, HttpHookDemuxTable.txn[hookid]);
+ TSHttpTxnHookAdd((TSHttpTxn)txn, hookid, instance->demux.txn[hookid]);
}
static int
TSLuaHttpHookRegister(lua_State * lua)
{
- TSHttpHookID hookid;
- LuaThreadInstance * lthread;
+ TSHttpHookID hookid;
- LuaLogDebug("[1]=%s [2]=%s", ltypeof(lua, 1), ltypeof(lua, 2));
hookid = (TSHttpHookID)luaL_checkint(lua, 1);
luaL_checktype(lua, 2, LUA_TFUNCTION);
@@ -449,33 +434,38 @@ TSLuaHttpHookRegister(lua_State * lua)
return -1;
}
- lthread = LuaGetThreadInstance();
- if (lthread == NULL) {
- lthread = tsnew<LuaThreadInstance>();
- lthread->lua = LuaPluginNewState(LuaPlugin);
- LuaSetThreadInstance(lthread);
- }
+ ScopedLuaState lstate(lua);
+ TSReleaseAssert(lstate);
- // Global hooks can only be registered once, but we load the Lua scripts in every thread. Check whether the hook has
- // already been registered and ignore any double-registrations.
- if (lthread->hooks[hookid] != LUA_NOREF) {
- TSReleaseAssert(HttpHookDemuxTable.global[hookid] != NULL);
+ // The lstate must match the current Lua state or something is seriously wrong.
+ TSReleaseAssert(lstate->lua == lua);
+
+ // Global hooks can only be registered once, but we load the Lua scripts in every thread. Check whether
+ // the hook has already been registered and ignore any double-registrations.
+ if (lstate->hookrefs[hookid] != LUA_NOREF) {
+ LuaLogDebug("ignoring double registration for %s hook", HttpHookName(hookid));
return 0;
}
- lthread->hooks[hookid] = luaL_ref(lua, LUA_REGISTRYINDEX);
+ // The callback function for the hook should be on the top of the stack now. Keep a reference
+ // to the callback function in the registry so we can pop it out later.
+ TSAssert(lua_type(lua, lua_gettop(lua)) == LUA_TFUNCTION);
+ lstate->hookrefs[hookid] = luaL_ref(lua, LUA_REGISTRYINDEX);
+
+ LuaLogDebug("%u/%p added hook ref %d for %s",
+ lstate->instance->instanceid, lua, lstate->hookrefs[hookid], HttpHookName(hookid));
- if (HttpHookDemuxTable.global[hookid] == NULL) {
- TSCont cont;
+ // We need to atomically install this global hook. We snaffle the high bit to mark whether or
+ // not it has been installed.
+ if (((uintptr_t)lstate->instance->demux.global[hookid] & 0x01u) == 0) {
+ TSCont cont = (TSCont)((uintptr_t)lstate->instance->demux.global[hookid] | 0x01u);
- cont = TSContCreate(LuaDemuxGlobalHook, TSMutexCreate());
- if (__sync_bool_compare_and_swap(&HttpHookDemuxTable.global[hookid], NULL, cont)) {
+ if (__sync_bool_compare_and_swap(&lstate->instance->demux.global[hookid],
+ lstate->instance->demux.global[hookid], cont)) {
LuaLogDebug("installed continuation for %s", HttpHookName(hookid));
- TSContDataSet(cont, (void *)hookid);
- TSHttpHookAdd(hookid, cont);
+ TSHttpHookAdd(hookid, (TSCont)((uintptr_t)cont & ~0x01u));
} else {
LuaLogDebug("lost hook creation race for %s", HttpHookName(hookid));
- TSContDestroy(cont);
}
}
@@ -499,21 +489,10 @@ LuaHookApiInit(lua_State * lua)
luaL_register(lua, NULL, LUAEXPORTS);
for (unsigned i = 0; i < TS_HTTP_LAST_HOOK; ++i) {
- if (HttpHookName((TSHttpHookID)i) == NULL) {
- // Unsupported hook, skip it.
- continue;
+ if (HttpHookName((TSHttpHookID)i) != NULL) {
+ // Register named constants for each hook ID.
+ LuaSetConstantField(lua, HttpHookName((TSHttpHookID)i), i);
}
-
- // Register named constants for each hook ID.
- LuaSetConstantField(lua, HttpHookName((TSHttpHookID)i), i);
- // Allocate txn and ssn continuations.
- HttpHookDemuxTable.txn[i] = TSContCreate(LuaDemuxTxnHook, NULL);
- HttpHookDemuxTable.ssn[i] = TSContCreate(LuaDemuxSsnHook, NULL);
- // And keep track of which hook each continuation was allocated for.
- TSContDataSet(HttpHookDemuxTable.txn[i], (void *)(uintptr_t)i);
- TSContDataSet(HttpHookDemuxTable.ssn[i], (void *)(uintptr_t)i);
-
- // Note that we allocate the global continuation table lazily so that we know when to add the hook.
}
return 1;
View
12 plugins/experimental/lua/hook.h
@@ -19,15 +19,21 @@
#ifndef LUA_HOOK_H_
#define LUA_HOOK_H_
+struct LuaPluginInstance;
+
// Pointer to LuaHttpSsnHookAdd() or LuaHttpTxnsnHookAdd().
-typedef void (*LuaHookAddFunction)(void *, TSHttpHookID);
+typedef void (*LuaHookAddFunction)(void *, const LuaPluginInstance *, TSHttpHookID);
-void LuaHttpSsnHookAdd(void *, TSHttpHookID);
-void LuaHttpTxnHookAdd(void *, TSHttpHookID);
+void LuaHttpSsnHookAdd(void *, const LuaPluginInstance *, TSHttpHookID);
+void LuaHttpTxnHookAdd(void *, const LuaPluginInstance *, TSHttpHookID);
// Set a LuaHookReference as the argument on the corresponding object. T can be either TSHttpSsn or TSHttpTxn.
template <typename T> void LuaSetArgReference(T ptr, int ref);
bool LuaRegisterHttpHooks(lua_State *, void *, LuaHookAddFunction, int);
+int LuaDemuxSsnHook(TSHttpHookID hookid, TSCont cont, TSEvent event, void * edata);
+int LuaDemuxTxnHook(TSHttpHookID hookid, TSCont cont, TSEvent event, void * edata);
+int LuaDemuxGlobalHook(TSHttpHookID hookid, TSCont cont, TSEvent event, void * edata);
+
#endif // LUA_HOOK_H_
View
72 plugins/experimental/lua/lutil.cc
@@ -23,23 +23,8 @@
#include <pthread.h>
#include <unistd.h>
-static thread_local_pointer<LuaThreadInstance> LuaThread;
-
-LuaPluginState * LuaPlugin;
int LuaHttpArgIndex;
-LuaThreadInstance::LuaThreadInstance()
- : lua(NULL)
-{
- for (unsigned i = 0; i < countof(this->hooks); ++i) {
- this->hooks[i] = LUA_NOREF;
- }
-}
-
-LuaThreadInstance::~LuaThreadInstance()
-{
-}
-
#if !defined(LUAJIT_VERSION)
static void *
LuaAllocate(void * ud, void * ptr, size_t osize, size_t nsize)
@@ -56,6 +41,12 @@ LuaAllocate(void * ud, void * ptr, size_t osize, size_t nsize)
#endif
lua_State *
+LuaNewState()
+{
+ return LuaPluginNewState();
+}
+
+lua_State *
LuaPluginNewState(void)
{
lua_State * lua;
@@ -78,42 +69,6 @@ LuaPluginNewState(void)
return lua;
}
-lua_State *
-LuaPluginNewState(LuaPluginState * plugin)
-{
- lua_State * lua;
-
- lua = LuaPluginNewState();
- if (lua == NULL) {
- return NULL;
- }
-
- if (!LuaPluginLoad(lua, plugin)) {
- lua_close(lua);
- return NULL;
- }
-
- return lua;
-}
-
-bool
-LuaPluginLoad(lua_State * lua, LuaPluginState * plugin)
-{
- for (LuaPluginState::pathlist::const_iterator p = plugin->paths.begin(); p < plugin->paths.end(); ++p) {
- if (access(p->c_str(), F_OK) != 0) {
- continue;
- }
-
- if (luaL_dofile(lua, p->c_str()) != 0) {
- // If the load failed, it should have pushed an error message.
- LuaLogError("failed to load Lua file %s: %s", p->c_str(), lua_tostring(lua, -1));
- return false;
- }
- }
-
- return true;
-}
-
void
LuaPushMetatable(lua_State * lua, const char * name, const luaL_Reg * exports)
{
@@ -171,15 +126,12 @@ LuaSetConstantField(lua_State * lua, const char * name, const char * value)
lua_setfield(lua, -2, name);
}
-LuaThreadInstance *
-LuaGetThreadInstance()
-{
- return LuaThread.get();
-}
-
void
-LuaSetThreadInstance(LuaThreadInstance * lthread)
+LuaDebugStack(lua_State * lua)
{
- LuaThread.set(lthread);
-}
+ int top = lua_gettop(lua);
+ for (int i = 1; i <= top; i++) {
+ LuaLogDebug("stack[%d] %s", i, ltypeof(lua, i));
+ }
+}
View
63 plugins/experimental/lua/lutil.h
@@ -25,12 +25,6 @@
#include <memory>
#include <pthread.h>
-
-struct LuaPluginState;
-struct LuaThreadInstance;
-
-extern LuaPluginState * LuaPlugin;
-
// Global argument index for TSHttpSsnArgGet and TSHttpTxnArgGet.
extern int LuaHttpArgIndex;
@@ -51,6 +45,9 @@ extern int LuaHttpArgIndex;
#define LuaLogError(fmt, ...) TSError(fmt, ##__VA_ARGS__)
#endif
+// Debug log the Lua stack.
+void LuaDebugStack(lua_State *);
+
// Return the type name string for the given index.
static inline const char *
ltypeof(lua_State * lua, int index) {
@@ -83,65 +80,13 @@ void LuaRegisterLibrary(lua_State * lua, const char * name, lua_CFunction loader
void LuaSetConstantField(lua_State * lua, const char * name, int value);
void LuaSetConstantField(lua_State * lua, const char * name, const char * value);
-// Get and set the per-thread lua_State.
-LuaThreadInstance * LuaGetThreadInstance();
-void LuaSetThreadInstance(LuaThreadInstance * lua);
-
// Allocate a new lua_State.
+lua_State * LuaNewState();
lua_State * LuaPluginNewState(void);
-lua_State * LuaPluginNewState(LuaPluginState * plugin);
-bool LuaPluginLoad(lua_State * lua, LuaPluginState * plugin);
-
-// Global Lua plugin state. Used to reconstruct new lua_States.
-struct LuaPluginState
-{
- typedef std::vector<std::string> pathlist;
-
- void init(unsigned argc, const char ** argv) {
- for (unsigned i = 0; i < argc; ++i) {
- paths.push_back(argv[i]);
- }
- }
-
- pathlist paths;
-};
-
-// Per-thread lua_State. Used to execute Lua-side code in ethreads.
-struct LuaThreadInstance
-{
- lua_State * lua;
- int hooks[TS_HTTP_LAST_HOOK];
-
- LuaThreadInstance();
- ~LuaThreadInstance();
-};
template <typename T, unsigned N> unsigned
countof(const T (&)[N]) {
return N;
}
-template <typename T>
-struct thread_local_pointer
-{
- thread_local_pointer() {
- TSReleaseAssert(pthread_key_create(&key, NULL) != -1);
- }
-
- ~thread_local_pointer() {
- pthread_key_delete(key);
- }
-
- T * get() const {
- return (T *)pthread_getspecific(key);
- }
-
- void set(T * t) const {
- pthread_setspecific(key, (void *)t);
- }
-
-private:
- pthread_key_t key;
-};
-
#endif // LUA_LUTIL_H_
View
25 plugins/experimental/lua/plugin.cc
@@ -19,12 +19,13 @@
#include <ts/ts.h>
#include "lutil.h"
#include "hook.h"
+#include "state.h"
extern "C" void
TSPluginInit(int argc, const char * argv[])
{
- LuaThreadInstance * lthread;
- TSPluginRegistrationInfo info;
+ TSPluginRegistrationInfo info;
+ instanceid_t instanceid;
info.plugin_name = (char *)"lua";
info.vendor_name = (char *)"Apache Traffic Server";
@@ -34,21 +35,11 @@ TSPluginInit(int argc, const char * argv[])
LuaLogError("Plugin registration failed");
}
- TSAssert(LuaPlugin == NULL);
-
// Allocate a TSHttpTxn argument index for handling per-transaction hooks.
- TSReleaseAssert(TSHttpArgIndexReserve(info.plugin_name, info.plugin_name, &LuaHttpArgIndex) == TS_SUCCESS);
-
- // Create the initial global Lua state.
- LuaPlugin = tsnew<LuaPluginState>();
- LuaPlugin->init((unsigned)argc, (const char **)argv);
-
- // Careful! We need to initialize the per-thread Lua state before we inject
- // any user code. User code will probably call TSAPI functions, which will
- // fetch or create the per-thread instance, which had better be available.
- lthread = tsnew<LuaThreadInstance>();
- lthread->lua = LuaPluginNewState();
- LuaSetThreadInstance(lthread);
- LuaPluginLoad(lthread->lua, LuaPlugin);
+ TSReleaseAssert(TSHttpArgIndexReserve("lua", "lua", &LuaHttpArgIndex) == TS_SUCCESS);
+
+ // Register a new Lua plugin instance, skipping the first argument (which is the plugin name).
+ instanceid = LuaPluginRegister((unsigned)argc - 1, (const char **)argv + 1);
+ TSReleaseAssert(instanceid == 0);
}
View
90 plugins/experimental/lua/remap.cc
@@ -18,28 +18,13 @@
#include <ts/ts.h>
#include <ts/remap.h>
-#include <unistd.h>
-#include <string.h>
#include "lapi.h"
#include "lutil.h"
+#include "state.h"
+#include <unistd.h>
+#include <pthread.h>
-static TSReturnCode
-LuaPluginRelease(lua_State * lua)
-{
- lua_getglobal(lua, "release");
- if (lua_isnil(lua, -1)) {
- // No "release" callback.
- return TS_SUCCESS;
- }
-
- if (lua_pcall(lua, 0, 0, 0) != 0) {
- LuaLogDebug("release failed: %s", lua_tostring(lua, -1));
- lua_pop(lua, 1);
- }
-
- lua_close(lua);
- return TS_SUCCESS;
-}
+static pthread_mutex_t PluginInstanceLock = PTHREAD_MUTEX_INITIALIZER;
static TSRemapStatus
LuaPluginRemap(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo * rri)
@@ -68,64 +53,47 @@ LuaPluginRemap(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo * rri)
return rq->status;
}
-void
-TSRemapDeleteInstance(void * ih)
-{
- lua_State * lua = (lua_State *)ih;
-
- if (lua) {
- LuaPluginRelease(lua);
- lua_close(lua);
- }
-}
-
TSReturnCode
TSRemapInit(TSRemapInterface * api_info, char * errbuf, int errbuf_size)
{
LuaLogDebug("loading lua plugin");
+
+ // Allocate a TSHttpTxn argument index for handling per-transaction hooks.
+ TSReleaseAssert(TSHttpArgIndexReserve("lua", "lua", &LuaHttpArgIndex) == TS_SUCCESS);
+
return TS_SUCCESS;
}
TSReturnCode
TSRemapNewInstance(int argc, char * argv[], void ** ih, char * errbuf, int errsz)
{
- LuaPluginState * remap;
- lua_State * lua;
-
- // Copy the plugin arguments so that we can use them to allocate a per-thread Lua state. It would be cleaner
- // to clone a Lua state, but there's no built-in way to do that, and to implement that ourselves would require
- // locking the template state (we need to manipulate the stack to copy values out).
- remap = tsnew<LuaPluginState>();
- remap->init((unsigned)argc, (const char **)argv);
-
- // Test whether we can successfully load the Lua program.
- lua = LuaPluginNewState(remap);
- if (!lua) {
- tsdelete(remap);
- return TS_ERROR;
- }
+ instanceid_t instanceid;
- *ih = remap;
+ pthread_mutex_lock(&PluginInstanceLock);
+
+ // Register a new Lua plugin instance, skipping the first two arguments (which are the remap URLs).
+ instanceid = LuaPluginRegister((unsigned)argc - 2, (const char **)argv + 2);
+ *ih = (void *)(intptr_t)instanceid;
+
+ pthread_mutex_unlock(&PluginInstanceLock);
return TS_SUCCESS;
}
-TSRemapStatus
-TSRemapDoRemap(void * ih, TSHttpTxn txn, TSRemapRequestInfo * rri)
+void
+TSRemapDeleteInstance(void * ih)
{
- LuaThreadInstance * lthread;
+ instanceid_t instanceid = (intptr_t)ih;
- // Find or clone the per-thread Lua state.
- lthread = LuaGetThreadInstance();
- if (!lthread) {
- LuaPluginState * lps;
-
- lps = (LuaPluginState *)ih;
- lthread = tsnew<LuaThreadInstance>();
+ pthread_mutex_lock(&PluginInstanceLock);
+ LuaPluginUnregister(instanceid);
+ pthread_mutex_unlock(&PluginInstanceLock);
+}
- LuaLogDebug("allocating new Lua state on thread 0x%llx", (unsigned long long)pthread_self());
- lthread->lua = LuaPluginNewState(lps);
- LuaSetThreadInstance(lthread);
- }
+TSRemapStatus
+TSRemapDoRemap(void * ih, TSHttpTxn txn, TSRemapRequestInfo * rri)
+{
+ ScopedLuaState lstate((intptr_t)ih);
- return LuaPluginRemap(lthread->lua, txn, rri);
+ TSReleaseAssert(lstate);
+ return LuaPluginRemap(lstate->lua, txn, rri);
}
View
333 plugins/experimental/lua/state.cc
@@ -0,0 +1,333 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include <ts/ts.h>
+#include <ts/remap.h>
+#include "state.h"
+#include "hook.h"
+#include "lutil.h"
+#include <unistd.h>
+#include <errno.h>
+
+#define INVALID_INSTANCE_ID (-1)
+
+typedef int (*LuaHookDemuxer)(TSHttpHookID, TSCont, TSEvent, void *);
+
+template <TSHttpHookID hookid, LuaHookDemuxer demuxer> int
+DemuxSpecificHook(TSCont cont, TSEvent event, void * edata) {
+ return demuxer(hookid, cont, event, edata);
+}
+
+template <LuaHookDemuxer demuxer> void
+InitDemuxTable(LuaPluginInstance::demux_table_t& table)
+{
+#define MakeLuaHook(demuxer, hookid) TSContCreate(DemuxSpecificHook<hookid, demuxer>, NULL)
+
+ table[TS_HTTP_READ_REQUEST_HDR_HOOK] = MakeLuaHook(demuxer, TS_HTTP_READ_REQUEST_HDR_HOOK);
+ table[TS_HTTP_OS_DNS_HOOK] = MakeLuaHook(demuxer, TS_HTTP_OS_DNS_HOOK);
+ table[TS_HTTP_SEND_REQUEST_HDR_HOOK] = MakeLuaHook(demuxer, TS_HTTP_SEND_REQUEST_HDR_HOOK);
+ table[TS_HTTP_READ_CACHE_HDR_HOOK] = MakeLuaHook(demuxer, TS_HTTP_READ_CACHE_HDR_HOOK);
+ table[TS_HTTP_READ_RESPONSE_HDR_HOOK] = MakeLuaHook(demuxer, TS_HTTP_READ_RESPONSE_HDR_HOOK);
+ table[TS_HTTP_SEND_RESPONSE_HDR_HOOK] = MakeLuaHook(demuxer, TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+ table[TS_HTTP_REQUEST_TRANSFORM_HOOK] = MakeLuaHook(demuxer, TS_HTTP_REQUEST_TRANSFORM_HOOK);
+ table[TS_HTTP_RESPONSE_TRANSFORM_HOOK]= MakeLuaHook(demuxer, TS_HTTP_RESPONSE_TRANSFORM_HOOK);
+ table[TS_HTTP_SELECT_ALT_HOOK] = MakeLuaHook(demuxer, TS_HTTP_SELECT_ALT_HOOK);
+ table[TS_HTTP_TXN_START_HOOK] = MakeLuaHook(demuxer, TS_HTTP_TXN_START_HOOK);
+ table[TS_HTTP_TXN_CLOSE_HOOK] = MakeLuaHook(demuxer, TS_HTTP_TXN_CLOSE_HOOK);
+ table[TS_HTTP_SSN_START_HOOK] = MakeLuaHook(demuxer, TS_HTTP_SSN_START_HOOK);
+ table[TS_HTTP_SSN_CLOSE_HOOK] = MakeLuaHook(demuxer, TS_HTTP_SSN_CLOSE_HOOK);
+ table[TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK] = MakeLuaHook(demuxer, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK);
+ table[TS_HTTP_PRE_REMAP_HOOK] = MakeLuaHook(demuxer, TS_HTTP_PRE_REMAP_HOOK);
+ table[TS_HTTP_POST_REMAP_HOOK] = MakeLuaHook(demuxer, TS_HTTP_POST_REMAP_HOOK);
+}
+
+// Global storage for Lua plugin instances. We vend instanceid_t's as an index into
+// this array.
+static std::vector<LuaPluginInstance *> LuaPluginStorage;
+
+template <typename T> struct is_integral_type {
+ enum { value = 0, is_pointer = 0 };
+};
+
+template <typename T> struct is_integral_type<T *> {
+ enum { value = 1, is_pointer = 1 };
+};
+
+static unsigned
+nproc()
+{
+ long count;
+
+ count = sysconf(_SC_NPROCESSORS_ONLN);
+ return (unsigned)std::max(count, 1l);
+}
+
+static unsigned
+thread_id()
+{
+ pthread_t self = pthread_self();
+
+ if (is_integral_type<pthread_t>::value) {
+ // If it's a pointer, then the lower bits are probably zero because it's
+ // likely to be 8 or 16 byte aligned.
+ if (is_integral_type<pthread_t>::is_pointer) {
+ return (unsigned)((intptr_t)self >> 4);
+ }
+ return (unsigned)(intptr_t)self;
+ } else {
+ // XXX make this work on FreeBSD!
+ TSReleaseAssert(0 && "unsupported platform");
+ return 0;
+ }
+}
+
+LuaPluginInstance::LuaPluginInstance()
+ : instanceid(INVALID_INSTANCE_ID), paths(), states()
+{
+}
+
+LuaPluginInstance::~LuaPluginInstance()
+{
+ this->invalidate();
+}
+
+void
+LuaPluginInstance::invalidate()
+{
+ for (unsigned i = 0; i < this->states.size(); ++ i) {
+ tsdelete(this->states[i]);
+ }
+
+ this->states.clear();
+ this->paths.clear();
+ this->instanceid = INVALID_INSTANCE_ID;
+
+ for (unsigned i = 0; i < countof(this->demux.global); ++i) {
+ TSContDestroy(this->demux.global[i]);
+ TSContDestroy(this->demux.ssn[i]);
+ TSContDestroy(this->demux.txn[i]);
+ this->demux.global[i] = this->demux.ssn[i] = this->demux.txn[i] = NULL;
+ }
+
+}
+
+void
+LuaPluginInstance::init(unsigned argc, const char ** argv)
+{
+ for (unsigned i = 0; i < argc; ++i) {
+ this->paths.push_back(argv[i]);
+ }
+
+ // Make sure we have enough threads to make concurrent access to lua
+ // states unlikely.
+ this->states.resize(nproc() * 2);
+
+ InitDemuxTable<LuaDemuxGlobalHook>(this->demux.global);
+ InitDemuxTable<LuaDemuxSsnHook>(this->demux.ssn);
+ InitDemuxTable<LuaDemuxTxnHook>(this->demux.txn);
+
+ for (unsigned i = 0; i < countof(this->demux.global); ++i) {
+ TSContDataSet(this->demux.global[i], (void *)(uintptr_t)this->instanceid);
+ TSContDataSet(this->demux.ssn[i], (void *)(uintptr_t)this->instanceid);
+ TSContDataSet(this->demux.txn[i], (void *)(uintptr_t)this->instanceid);
+ }
+
+}
+
+instanceid_t
+LuaPluginRegister(unsigned argc, const char ** argv)
+{
+ instanceid_t instanceid = INVALID_INSTANCE_ID;
+ LuaPluginInstance * plugin;
+
+ LuaLogDebug("registering plugin");
+ // OK, first we try to find an unused instance slot.
+ for (unsigned i = 0; i < LuaPluginStorage.size(); ++i) {
+ if (LuaPluginStorage[i]) {
+ // This slot looks ok, let's try to claim it.
+ instanceid = i;
+ break;
+ }
+ }
+
+ // Take the current instanceid, incrementing it for next time.
+ if (instanceid == INVALID_INSTANCE_ID) {
+ instanceid = LuaPluginStorage.size();
+ LuaPluginStorage.resize(LuaPluginStorage.size() + 1);
+ }
+
+ // Mark this plugin instance as in use.
+ plugin = LuaPluginStorage[instanceid] = tsnew<LuaPluginInstance>();
+ plugin->instanceid = instanceid;
+
+ // The path list should be empty if we correctly released it last time this
+ // instance ID was used.
+ TSReleaseAssert(plugin->paths.empty());
+ LuaPluginStorage[instanceid]->init(argc, argv);
+
+ // Allocate the Lua states, then separately initialize by evaluating all the Lua files.
+ for (unsigned i = 0; i < plugin->states.size(); ++i) {
+ plugin->states[i] = tsnew<LuaThreadState>();
+ plugin->states[i]->alloc(plugin, i);
+ }
+
+ for (unsigned i = 0; i < LuaPluginStorage[instanceid]->states.size(); ++i) {
+ plugin->states[i]->init(plugin);
+ }
+
+ return instanceid;
+}
+
+void
+LuaPluginUnregister(instanceid_t instanceid)
+{
+ TSReleaseAssert(instanceid < LuaPluginStorage.size());
+ tsdelete(LuaPluginStorage[instanceid]);
+ LuaPluginStorage[instanceid] = NULL;
+}
+
+LuaThreadState::LuaThreadState()
+ : lua(NULL), instance(NULL)
+{
+ pthread_mutexattr_t attr;
+
+ // We need a recursive mutex so that we can safely reacquire it from Lua code.
+ TSReleaseAssert(pthread_mutexattr_init(&attr) == 0);
+ TSReleaseAssert(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0);
+ TSReleaseAssert(pthread_mutex_init(&this->mutex, &attr) == 0);
+
+ for (unsigned i = 0; i < countof(this->hookrefs); ++i) {
+ this->hookrefs[i] = LUA_NOREF;
+ }
+
+ pthread_mutexattr_destroy(&attr);
+}
+
+LuaThreadState::~LuaThreadState()
+{
+ this->release();
+ pthread_mutex_destroy(&this->mutex);
+}
+
+bool
+LuaThreadState::alloc(LuaPluginInstance * plugin, unsigned threadid)
+{
+ this->lua = LuaNewState();
+ this->instance = plugin;
+
+ // Push the instanceid into a global integer. We will use this later to rendevous
+ // with the lthread from the lua_State. We have to set the instanceid global before
+ // executing any Lua code, because that will almost certainly call back into the plugin
+ // ad reguire the instance id to be set.
+ lua_pushinteger(this->lua, plugin->instanceid);
+ lua_setfield(this->lua, LUA_REGISTRYINDEX, "__instanceid");
+
+ lua_pushinteger(this->lua, threadid);
+ lua_setfield(this->lua, LUA_REGISTRYINDEX, "__threadid");
+
+ return true;
+}
+
+bool
+LuaThreadState::init(LuaPluginInstance * plugin)
+{
+ for (LuaPluginInstance::pathlist_t::const_iterator p = plugin->paths.begin(); p < plugin->paths.end(); ++p) {
+ if (access(p->c_str(), F_OK) != 0) {
+ LuaLogError("%s: %s", p->c_str(), strerror(errno));
+ continue;
+ }
+
+ if (luaL_dofile(this->lua, p->c_str()) != 0) {
+ // If the load failed, it should have pushed an error message.
+ LuaLogError("failed to load Lua file %s: %s", p->c_str(), lua_tostring(lua, -1));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+LuaThreadState::release()
+{
+ if (this->lua) {
+ lua_close(this->lua);
+ this->lua = NULL;
+ }
+}
+
+std::pair<LuaThreadState *, LuaPluginInstance *>
+LuaThreadStateAcquire(lua_State * lua)
+{
+ LuaThreadState * lthread;
+ LuaPluginInstance * instance;
+ instanceid_t instanceid;
+ unsigned threadid;
+
+ lua_getfield(lua, LUA_REGISTRYINDEX, "__instanceid");
+ instanceid = (instanceid_t)luaL_checkinteger(lua, -1);
+
+ lua_getfield(lua, LUA_REGISTRYINDEX, "__threadid");
+ threadid = (unsigned)luaL_checkinteger(lua, -1);
+
+ TSReleaseAssert(instanceid < LuaPluginStorage.size());
+
+ instance = LuaPluginStorage[instanceid];
+
+ TSReleaseAssert(threadid < instance->states.size());
+ lthread = instance->states[threadid];
+
+ LuaLogDebug("%u/%p acquired state %u from plugin instance %u on thread %u",
+ instanceid, lthread->lua, threadid, instanceid, thread_id());
+
+ lua_pop(lua, 2);
+
+ // Since we already hav a lua_State, we must already be holding the lock. But acquire
+ // and release come in matched pairs, so we need a recursive lock to release.
+ TSReleaseAssert(pthread_mutex_lock(&lthread->mutex) == 0);
+ return std::make_pair(lthread, instance);
+}
+
+std::pair<LuaThreadState *, LuaPluginInstance *>
+LuaThreadStateAcquire(instanceid_t instanceid)
+{
+ LuaThreadState * lthread;
+ LuaPluginInstance * instance;
+ unsigned which;
+
+ TSReleaseAssert(instanceid < LuaPluginStorage.size());
+
+ instance = LuaPluginStorage[instanceid];
+
+ // Index the set of LuaThreadStates with the thread ID. We might want to do a proper
+ // hash on this to prevent false sharing.
+ which = thread_id() % instance->states.size();
+ lthread = instance->states[which];
+
+ LuaLogDebug("%u/%p acquired state %u from plugin instance %u on thread %u",
+ instanceid, lthread->lua, which, instanceid, thread_id());
+
+ TSReleaseAssert(pthread_mutex_lock(&lthread->mutex) == 0);
+ return std::make_pair(lthread, instance);
+}
+
+void
+LuaThreadStateRelease(LuaThreadState * lthread)
+{
+ TSReleaseAssert(pthread_mutex_unlock(&lthread->mutex) == 0);
+}
View
157 plugins/experimental/lua/state.h
@@ -0,0 +1,157 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef LUA_STATE_H_
+#define LUA_STATE_H_
+
+#include <string>
+#include <vector>
+#include <utility>
+#include <pthread.h>
+#include <lua.hpp>
+
+/*
+
+Lua Plugin threading model
+
+For remapping, we need to support multiple indepedent Lua plugin
+instances. Each instance is handled by a LuaPluginInstance object.
+Each plugin instance maintains a pool of lua_States which are
+independent Lua interpeters. The LuaThreadState object owns a single
+lua_State, holding additional hook data that is needed to de-multiplex
+events.
+
+There are two basic code paths to obtaining a LuaThreadState. If
+we already have a lua_State, then we can use the __instanceid and
+__threadid global variables to identify the LuaThreadState object.
+If we don't have a lua_State, then we know the instance ID from the
+hook continuation data (attached per LuaPluginInstance), and we
+choose a state by hashing the thread ID.
+
+ Traffic Server +-> LuaPluginInstance[0]
+ | +-> LuaThreadState[0]
+ | +-> LuaThreadState[1]
+ | +-> LuaThreadState[2]
+ | +-> LuaThreadState[3]
+ |
+ +-> LuaPluginInstance[1]
+ | +-> LuaThreadState[0]
+ | +-> LuaThreadState[1]
+ | +-> LuaThreadState[2]
+ | +-> LuaThreadState[3]
+ |
+ +-> LuaPluginInstance[2]
+ +-> LuaThreadState[0]
+ +-> LuaThreadState[1]
+ +-> LuaThreadState[2]
+ +-> LuaThreadState[3]
+
+*/
+
+typedef uint32_t instanceid_t;
+
+struct LuaThreadState;
+struct LuaPluginInstance;
+
+// Per-thread lua_State. Used to execute Lua-side code in ethreads.
+struct LuaThreadState
+{
+ lua_State * lua;
+ int hookrefs[TS_HTTP_LAST_HOOK];
+ LuaPluginInstance * instance;
+
+ pthread_mutex_t mutex;
+
+ LuaThreadState();
+ ~LuaThreadState();
+
+ bool alloc(LuaPluginInstance *, unsigned);
+ bool init(LuaPluginInstance *);
+ void release();
+
+private:
+ LuaThreadState(const LuaThreadState&); // disable
+ LuaThreadState& operator=(const LuaThreadState&); // disable
+};
+
+struct LuaPluginInstance
+{
+ typedef std::vector<std::string> pathlist_t;
+ typedef TSCont demux_table_t[TS_HTTP_LAST_HOOK];
+
+ LuaPluginInstance();
+ ~LuaPluginInstance();
+
+ void invalidate();
+ void init(unsigned argc, const char ** argv);
+
+ struct {
+ demux_table_t global;
+ demux_table_t txn;
+ demux_table_t ssn;
+ } demux;
+
+ instanceid_t instanceid;
+ pathlist_t paths;
+ std::vector<LuaThreadState *> states;
+
+private:
+ LuaPluginInstance(const LuaPluginInstance&); // disable
+ LuaPluginInstance& operator=(const LuaPluginInstance&); // disable
+};
+
+instanceid_t LuaPluginRegister(unsigned argc, const char ** argv);
+void LuaPluginUnregister(instanceid_t instanceid);
+
+// Acquire a locked Lua thread state belonging to the given instance.
+std::pair<LuaThreadState *, LuaPluginInstance *> LuaThreadStateAcquire(instanceid_t);
+std::pair<LuaThreadState *, LuaPluginInstance *> LuaThreadStateAcquire(lua_State *);
+// Return the previously acquired Lua thread state.
+void LuaThreadStateRelease(LuaThreadState *);
+
+struct ScopedLuaState
+{
+ explicit ScopedLuaState(instanceid_t instanceid)
+ : ptr(LuaThreadStateAcquire(instanceid)) {
+ }
+
+ explicit ScopedLuaState(lua_State * lua)
+ : ptr(LuaThreadStateAcquire(lua)) {
+ }
+
+ ~ScopedLuaState() {
+ LuaThreadStateRelease(this->ptr.first);
+ }
+
+ operator bool() const {
+ return this->ptr.first != NULL;
+ }
+
+ LuaThreadState * operator->() const {
+ return this->ptr.first;
+ }
+
+ LuaPluginInstance * instance() const {
+ return this->ptr.second;
+ }
+
+private:
+ std::pair<LuaThreadState *, LuaPluginInstance *> ptr;
+};
+
+#endif // LUA_STATE_H_
Please sign in to comment.
Something went wrong with that request. Please try again.