The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

Wednesday, May 20, 2015

If you think signal handling in C sucks, try Lua

So I have this Lua module to handle signals I wrote …

Originally, I just set a flag that had to be manually checked, as that was the safest thing to do (make that “practically the only thing you can do” if you want to be pedantic about it).

But after a while, I thought it would be nice to write a handler in Lua and not have to manually check a flag. Unfortunately, signal handlers have to be thought of as asynchronous threads that run at the worst possible time, which means calling into Lua from a signal handler is … not a good idea. The way to handle this is to hook into the Lua VM in the signal handler. lua_sethook() is the only safe Lua function to call from an asynchronous thread (or signal handler) as it just sets a flag in the Lua VM. Then when the Lua VM is at a safe spot, the hook is call which can then run the Lua function.

So we write our Lua function:

function Lua_signal_handler()
  print("Hi!  I'm handing a signal!")
end

and here I'm making it a global function just for illustrative purposes. At some point, we catch the signal to install the signal handler (which has to be in C):

/*------------------------------------------------------------------------
; In order to hook the Lua VM, we need to statically store the Lua state. 
; That's what this variable is here for ...
;-----------------------------------------------------------------------*/

static lua_State *gL;

	/* ... code code blah blah ... */

	/*----------------------------------------------------------------
	; we're in some function and we have access to a Lua state.  Store
	; it so the signal handler can reference it.
	;----------------------------------------------------------------*/

	gL = L;

	/*---------------------------------------------------------------
	; sigaction() should be used, but that's quite a bit of overhead
	; just to make a point.  Anyway, we are installing our signal
	; handler.
	;--------------------------------------------------------------*/

	signal(SIGINT,C_signal_handler);

The signal handler installs a hook:

static void C_signal_handler(int sig)
{
  lua_sethook(gL,luasignalhook,LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT,1);
}

and when it's safe for the VM to call the hook, it does, which then calls our signal handler written in Lua:

static void luasignalhook(lua_State *L,lua_Debug *ar)
{
  /*------------------------------------------------------------------
  ; remove the hook as we don't want to be called over and over again
  ;------------------------------------------------------------------*/

  lua_sethook(L,NULL,0,0);

  /*--------------------------------------------------------------------------
  ; get our function (which is a global, just for illustrative purposes, and
  ; call it.
  ;--------------------------------------------------------------------------*/

  lua_getglobal(L,"Lua_signal_handler");
  lua_call(L,1,0);
}

Yes, it's a rather round-about way to handle a signal, but that's what is required to run Lua code as a signal handler. And it works except for two cases (that I have so far identified—there might be more).

The first case—coroutines. Lua coroutines can be thought of as threads, but unlike system threads, they have to be scheduled manually. And like system threads, signals and coroutines don't mix. Each coroutine creates a new Lua state, which means that if a signal happens, the Lua state that is hooked may not be the one that is currently running and thus, the Lua-written signal handler may never be called!

The second issue involves a feature of POSIX signals—the ability to restart system calls. Normally, a signal will interrupt a system call and its up to the program to restart it. There is an option to restart a system call automatically when a signal happens so the program doesn't have to deal with it. The funny thing is—under the right conditions, the Lua signal handler is never called! Say the program is making a long system call, such as waiting for a network packet to arrive. A signal is raised, our signal handler is called, which hooks the Lua VM. Then the system call is resumed. Until a packet arrives (and thus we return from the system call) the Lua VM never gets control, and thus, our function to handle the signal is never called (or called way past when it should have been called).

Fortunately, I found these issues out in testing, not in production code. But it has me thinking that I should probably work to avoid using signals if at all possible.

Obligatory Picture

[The future's so bright, I gotta wear shades]

Obligatory Contact Info

Obligatory Feeds

Obligatory Links

Obligatory Miscellaneous

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: https://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

https://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2024 by Sean Conner. All Rights Reserved.