lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Mon, Aug 11, 2014 at 09:08:28PM -0400, Sean Conner wrote:
<snip>
>   Not directly, but you can still gain control via a signal handler.  Some
> basic code:
<snip>
>       volatile int          ghookcount;
>       volatile int          ghookmask;
>       volatile lua_Hook     ghook;
>       volatile_sig_atomic_t gsig;
<snip>
> 	void signal_handler(int sig)
> 	{
> 	  if (!gsig)
> 	  {
> 	    gsig       = sig;
> 	    ghookcount = lua_gethookcount(gL);
> 	    ghookmask  = lua_gethookmask(gL);
> 	    ghook      = lua_gethook(gL);
> 	    lua_sethook(gL,luasigbackend,LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT,1);
> 	  }
> 	}

This can't be async-signal-safe unless all other signals are blocked during
execution of the handler. So there should be the caveat that when installing
the signal handler, sigfillset is used to initialize sa_mask.

> 
>   Yeah, it's a bit of a round-about way of doing things, but it appears to
> tbe the safest way of handling signals in Lua.  I've done two
> implementations based on this method, on for ANSI-C only:

The safest way is to not handle signals asynchronously at all. There are two
ways to do this on Unix systems. One is to block all signals and then use
sigtimedwait to query for pending signals, which will clear each pending
signal atomically. sigtimedwait is available on most systems. The only
systems lacking this, AFAIK, are OS X and OpenBSD. They offer sigwait, but
sigwait will block the process. Fortunately sigtimedwait can be safely
emulated on OS X and OpenBSD, at least as long as only one thread manages
signal disposition. In my Lua Unix module sigtimedwait is supported on all
the platforms--Linux, *BSD, OS X, Solaris, and AIX.

But the problem with sigtimedwait is that you often want some sort of
concurrent execution of signal handlers without having to explicitly query
them. My cqueues library provides an API for kernel-based, event-oriented
signal management. Internally it uses signalfd on Linux and kqueue
EVFILT_SIGNAL on BSDs (including OS X). On systems like Solaris it queries
sigtimedwait on a timer. The following example will work on all those
systems:

	local signal = require"cqueues.signal"
	local sigfd = signal.listen(signal.SIGTERM, signal.SIGINT)

	while true do
		local signo = sigfd:wait() -- doesn't block process

		print(signo)
	end

The nice thing about using sigtimedwait or signalfd/kqueue is that you never
need to maintain any global state. (There's no "pipe trick" happening here,
either.) So you can put that support into a module without worrying that it
can mess up the state of some other module. The application will still want
to setup signal disposition appropriately (specifically block all relevant
signals in all threads), but there are no hidden side effects or global
state in either C or Lua.