#5220 Events posted by Tcl_QueueEvent linger in queue

current: 8.6.0
open
Kevin B KENNY
5
2013-03-24
2013-03-24
Schelte Bron
No

My C library posts an event via Tcl_QueueEvent when the app receives a signal (like SIGHUP). When using the library with a non-threaded tclsh, the event is picked up by the event loop and processed almost immediately. However, with a threaded tclsh nothing happens until some other event occurs. At that time the queued events and the other event are all processed. Is this the intended behavior?

Issue found on OpenSUSE 12.3 (linux 3.7.10) 64-bit.

I have attached the stripped down sources for a library that reproduces the problem. Run the script and send a SIGHUP to the process. Observe that with a non-threaded tclsh the script reports "signal" immediately. With a threaded tclsh the "signal" report comes out just before the "Boo!".

Discussion

  • Schelte Bron
    Schelte Bron
    2013-03-24

    Simplified library sources to reproduce the problem.

     
    Attachments
  • Joe English
    Joe English
    2013-03-24

    The signal handler in the attached code calls ckalloc() and Tcl_QueueEvent(), neither of which are async-signal safe.

    Probable cause of problem in threaded build is that nothing calls Tcl_AlertNotifier(), so the event loop never wakes up. Note that Tcl_AlertNotifier() isn't async-signal safe either.

    Recommended approach is to use Tcl_CreateFileHandler and the write-to-pipe technique.

     
  • Schelte Bron
    Schelte Bron
    2013-03-24

    Thanks for your suggestions Joe, although I don't know what "the write-to-pipe technique" is in this context.

    I switched to Tcl_AsyncMark for my library, because apparently that command was specifically designed to handle cases like this and it seems to work just fine.

    If the consensus is that the difference in behavior of Tcl_QueueEvent in threaded and non-threaded builds is not a bug then I don't have objections to closing this tracker item.

     
  • Joe English
    Joe English
    2013-03-25

    The idea behind write-to-pipe is: use pipe(2) to get a pair of connected file descriptors; in the event loop listen on the read end (with select, poll, kqueue, whatever), and in the signal handler write a single byte (generally the signal number) to the write end. That will wake up the event loop, which can read the pipe to determine which signal was sent.

    write(2) is async-signal safe, whereas almost none of the pthread_* routines are. That's why this approach is preferred.

    > I switched to Tcl_AsyncMark for my library, because apparently
    > that command was specifically designed to handle cases like this

    That may have been the original intent, but since the first thing Tcl_AsyncMark() does is lock a mutex, it can't be called from a signal handler either.

    > and it seems to work just fine.

    "seems" being the operative word here :-) If a signal ever gets delivered while Tcl is in the middle of an AsyncInvoke or other critical section, the process will deadlock.

    Also beware: async handlers are called at very precarious times; the interp can be in almost any state, you have to be very careful. (Scheduling a callback with Tcl_QueueEvent is about the only thing you should do in an async handler).

     
  • Schelte Bron
    Schelte Bron
    2013-03-25

    Then the Tcl_AsyncMark manual page is misleading. It says: "These procedures provide a safe mechanism for dealing with asynchronous events such as signals". It would be good to note there that the mechanism is not entirely safe.

     
  • Part of the problem was that the mechanism was originally (in the distant past) designed specifically for signal handling. It was also abused quite a bit for passing in messages from other threads (especially in non-threaded-Tcl in threaded-app situations) which is a very different use.

    What I want to see is some way to improve signal delivery. Some signals probably should just go on the event queue (SIGWINCH anyone?) but others are more urgent (SIGINT) and some just lethal (SIGSEGV+SIGBUS are unlikely to ever be recoverable from by script). But we may have to throw some of the babies under the bus to make this happen; it's very messy and I've never 100% understood it (or certainly not enough to grok that I *do* understand it).

     

  • Anonymous
    2013-04-03

    @Joe,
    can you provide an example of how to use the write-to-pipe technique.
    Thanx,
    Nicolas

     
    Last edit: Anonymous 2 days ago
  • Tcl_AsyncMark might be helpful here. Not a complete solution, but part of a method