diff --git a/ldebug.c b/ldebug.c index b8a0ecbde..ed9900e04 100644 --- a/ldebug.c +++ b/ldebug.c @@ -169,12 +169,12 @@ LUA_API int lua_gethookcount (lua_State *L) { ** gcc ensures that for all platforms where it runs). */ LUA_API void lua_setglobalhook (lua_State *L, lua_Hook func) { - G(L)->globalhook = func; + LUA_ATOMIC_STORE_RELEASE(G(L)->globalhook, func); } LUA_API lua_Hook lua_getglobalhook (lua_State *L) { - return G(L)->globalhook; + return LUA_ATOMIC_LOAD_ACQUIRE(G(L)->globalhook); } @@ -810,7 +810,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) { int luaG_traceexec (lua_State *L, const Instruction *pc) { CallInfo *ci = L->ci; - lua_Hook globalhook = G(L)->globalhook; + lua_Hook globalhook = LUA_ATOMIC_LOAD_ACQUIRE(G(L)->globalhook); lu_byte mask = L->hookmask; int counthook; if (!globalhook && !(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ diff --git a/ldo.c b/ldo.c index c4e406cfa..2a3027ae1 100644 --- a/ldo.c +++ b/ldo.c @@ -311,7 +311,7 @@ void luaD_hook (lua_State *L, lua_Hook hook, int event, int line, ** active. */ void luaD_hookcall (lua_State *L, CallInfo *ci) { - lua_Hook globalhook = G(L)->globalhook; + lua_Hook globalhook = LUA_ATOMIC_LOAD_ACQUIRE(G(L)->globalhook); int hook = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL; Proto *p; if (!globalhook && !(L->hookmask & LUA_MASKCALL)) /* some other hook? */ @@ -329,7 +329,7 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) { static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) { ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ int delta = 0; - lua_Hook globalhook = G(L)->globalhook; + lua_Hook globalhook = LUA_ATOMIC_LOAD_ACQUIRE(G(L)->globalhook); if (isLuacode(ci)) { Proto *p = clLvalue(s2v(ci->func))->p; if (p->is_vararg) @@ -472,7 +472,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) { Cfunc: { int n; /* number of returns */ CallInfo *ci; - lua_Hook globalhook = G(L)->globalhook; + lua_Hook globalhook = LUA_ATOMIC_LOAD_ACQUIRE(G(L)->globalhook); checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ ci = next_ci(L); ci->nresults = nresults; diff --git a/lstate.c b/lstate.c index 09bd6c6b0..5c6b5e6b6 100644 --- a/lstate.c +++ b/lstate.c @@ -419,7 +419,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->gcstepsize = LUAI_GCSTEPSIZE; setgcparam(g->genmajormul, LUAI_GENMAJORMUL); g->genminormul = LUAI_GENMINORMUL; - g->globalhook = NULL; + LUA_ATOMIC_STORE_RELEASE(g->globalhook, NULL); for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ diff --git a/lstate.h b/lstate.h index f49d4dc8d..843764521 100644 --- a/lstate.h +++ b/lstate.h @@ -272,7 +272,7 @@ typedef struct global_State { lua_WarnFunction warnf; /* warning function */ void *ud_warn; /* auxiliary data to 'warnf' */ unsigned int Cstacklimit; /* current limit for the C stack */ - volatile lua_Hook globalhook; + LUA_ATOMIC(lua_Hook) globalhook; } global_State; diff --git a/lua.c b/lua.c index 2269a8307..dc343cb7b 100644 --- a/lua.c +++ b/lua.c @@ -32,7 +32,7 @@ #define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX -static lua_State *globalL = NULL; +static LUA_ATOMIC(lua_State *) globalL; static const char *progname = LUA_PROGNAME; @@ -47,6 +47,16 @@ static void lstop (lua_State *L, lua_Debug *ar) { } +/* +** If pointers aren't lock-free, then signal handlers can't safely set them, +** since what they interrupted might be holding the lock they need to take, +** resulting in a deadlock. Fail at compile time if we detect that. +*/ +#if defined(ATOMIC_POINTER_LOCK_FREE) && ATOMIC_POINTER_LOCK_FREE != 2 +#error "Hooks can't be safely set from signal handlers!" +#endif + + /* ** Function to be called at a C signal. Because a C signal cannot ** just change a Lua state (as there is no proper synchronization), @@ -55,7 +65,7 @@ static void lstop (lua_State *L, lua_Debug *ar) { */ static void laction (int i) { signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ - lua_setglobalhook(globalL, lstop); + lua_setglobalhook(LUA_ATOMIC_LOAD_ACQUIRE(globalL), lstop); } @@ -133,7 +143,7 @@ static int docall (lua_State *L, int narg, int nres) { int base = lua_gettop(L) - narg; /* function index */ lua_pushcfunction(L, msghandler); /* push message handler */ lua_insert(L, base); /* put it under function and args */ - globalL = L; /* to be available to 'laction' */ + LUA_ATOMIC_STORE_RELEASE(globalL, L); /* to be available to 'laction' */ signal(SIGINT, laction); /* set C-signal handler */ status = lua_pcall(L, narg, nres, base); signal(SIGINT, SIG_DFL); /* reset C-signal handler */ diff --git a/lua.h b/lua.h index 8c0c4ea89..91ece9b46 100644 --- a/lua.h +++ b/lua.h @@ -29,6 +29,38 @@ #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" +/* +** Stuff to use atomics if able, or falling back to the technically-incorrect +** but probably good enough "volatile" if not +*/ +#if defined(__cplusplus) && __cplusplus >= 201103L + +/* C++11 atomics */ +#include + +#define LUA_ATOMIC(t) std::atomic +#define LUA_ATOMIC_LOAD_ACQUIRE(obj) obj.load(std::memory_order_acquire) +#define LUA_ATOMIC_STORE_RELEASE(obj, desired) obj.store(desired, std::memory_order_release) + +#elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) + +/* C11 atomics */ +#include + +#define LUA_ATOMIC(t) _Atomic(t) +#define LUA_ATOMIC_LOAD_ACQUIRE(obj) atomic_load_explicit(&obj, memory_order_acquire) +#define LUA_ATOMIC_STORE_RELEASE(obj, desired) atomic_store_explicit(&obj, desired, memory_order_release) + +#else + +/* no atomics */ +#define LUA_ATOMIC(t) t volatile +#define LUA_ATOMIC_LOAD_ACQUIRE(obj) (obj) +#define LUA_ATOMIC_STORE_RELEASE(obj, desired) (obj = desired) + +#endif + + /* mark for precompiled code ('Lua') */ #define LUA_SIGNATURE "\x1bLua" diff --git a/lvm.c b/lvm.c index 37e83e2b7..466142c4e 100644 --- a/lvm.c +++ b/lvm.c @@ -1044,7 +1044,7 @@ void luaV_finishOp (lua_State *L) { -#define updatetrap(ci) (trap = ci->u.l.trap || G(L)->globalhook) +#define updatetrap(ci) (trap = ci->u.l.trap || LUA_ATOMIC_LOAD_ACQUIRE(G(L)->globalhook)) #define updatebase(ci) (base = ci->func + 1) @@ -1134,7 +1134,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { #include "ljumptab.h" #endif tailcall: - trap = L->hookmask || G(L)->globalhook; + trap = L->hookmask || LUA_ATOMIC_LOAD_ACQUIRE(G(L)->globalhook); cl = clLvalue(s2v(ci->func)); k = cl->p->k; pc = ci->u.l.savedpc; diff --git a/makefile b/makefile index 2c68f4547..e63ccc7a6 100644 --- a/makefile +++ b/makefile @@ -53,7 +53,7 @@ LOCAL = $(TESTS) $(CWARNS) # enable Linux goodies -MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX -DLUA_USE_READLINE +MYCFLAGS= $(LOCAL) -std=c11 -DLUA_USE_LINUX -DLUA_USE_READLINE MYLDFLAGS= $(LOCAL) -Wl,-E MYLIBS= -ldl -lreadline