back to article Safe signals in Perl

Once upon a time, handling signals in Perl code had a pretty big gotcha — one that you couldn’t work around. Perl 5.8 changed signal handling in a way that eliminated that gotcha, but replaced it with a different one, harder to trigger, but no less surprising. The original gotcha Signals are delivered asynchronously — by …

COMMENTS

This topic is closed for new posts.
  1. Anonymous Coward
    Anonymous Coward

    Signals?

    Whilst the signals issue is significant, it is in fact quite sensible, Perl isn't delivering the signal while it's running locked code. All well and good.

    Perhaps an alternative solution is to run the broken code in a sub process with a watchdog killing that process if it overruns? No whacky signal handling required.

    These days with threads being trendy we forget that the plus point of processes is that they are supposed to be separated from each other.

  2. David Dawson
    IT Angle

    Hooray!

    Go async madness!

  3. Anonymous Coward
    Anonymous Coward

    Sounds to me like the solution is obvious...

    If a library can go into an infinite loop while reading a file, no matter how mangled that file is, that is a bug. A big part of the reason for using libraries is to get more robust and reliable behaviour than you could reasonably develop from scratch for yourself, and that especially counts when handling potentially mangled inputs. Report the bug and either develop and submit a fix, or else hope for someone else to do it.

    I never said it was a quick solution - just an obvious one.

  4. Christopher E. Stith

    Perl 5.8.0 was released July 18th, 2002

    So at least it took a while for this signal handling to seriously bite someone knowledgeable about Perl. The previous signals were a nuisance, but still useful. This newer method is an improvement in almost every case. It is probably close to the best one can ask of an opcode-compiled language on a VM. Signal, by their nature, can be problematic even at the level of C, although their usefulness often outweighs any trouble they may cause in edge cases.

  5. Eddie Edwards
    Flame

    whoosh

    The problem is not signals, the problem is working around bugs in libraries in this hideous way. Yeah, let's BREAK SIGNALS to get it working. FFS.

    An interpreted language cannot stop foreign code while it is executing. That's not something you work around with interrupts, it's something you respect, if you've got any common sense at all.

    So you raise an interrupt and physically prevent the code from continuing. This leaves arbitrary amounts of leaked memory behind, may leave OS libraries in an undefined state, is going to lead to exactly the kind of "hard to track down" bug that the new signals were supposed to avoid.

    I wouldn't wish this bug on an embedded C programmer with 30 years of experience. A Perl programmer? Jesus H Christ.

    You know, sometimes language designers actually know what they're doing.

  6. James
    Stop

    What the hell

    Signals in Perl! Why?

    If some colleague handed me a Perl script that had all this shit in it, it would never be used.

  7. Anonymous Coward
    IT Angle

    WTF!!!

    A workround for a bug in library code, nope, I've never seen that before.....

    How about fixing the library code bug DOH!!!

    "Then at suitable safe moments, it checks whether any signals have been delivered" Can I suggest that we call this "DoEvents", we can dot them all over the code..... hang on that looks familiar.

  8. Kevin Reader
    Pirate

    So when they fixed it, it was still broken

    So the nifty better signals were infact a kludge then. In the desire to prevent signal handling damaging the code, they actually threw out the real feature of signals - that they can interrupt anything AND might well need to.

    It would have been far far better to implement a special data structure or signal FIFO that allowed the signal handler to interface cleanly with your main code (which ought to know when it can safely notice). What happened ofcourse was that someone moved the decision point from "your code" where you "needed to know how to write a signal handler properly and safely" to "their internal code" where you get a simplified and less useful version of signals. They now only go off at the times some PerlGod decided they ought to. And as you demonstrate this means you lose all control when external libraries and processes are used EVEN THOUGH those are the two main times to worry about control, signals, timeouts and interrupts.

    Did the PerlGods hire a M$ programmer to design this new signal handler? It reduces realtime-signals to the status of windows-message-passing. That's not a fix its a different beast entirely. As I said a far better solution would have been implementing a standard-signal-fifo or standard-event-queue. That would have avoided the data corruption risk while preserving as much of the realtime-nature as the program developer required.

    It reminds me of a time at Reuter's I think - where I eventually uncovered that windows (3.12 or 386 I think) handled certain things (like keyboard interrupts) while it was sometimes in an obscure part of the kernel with its own very very small stack. So when complicated code was added for trader's keyboards the stack blew bigtime! Oh, AND an idiot had implemented the keyboard interrupt code so that it could go recursive in the final two instructions! It was a nice combination. It was an interesting fix too, and rather better thought out than the perl signal handler.

    I concur with posters who felt that an XML parser ought not to hang on bogus input!! All the XML parser's I've come across seem to HUGE, SLOW and frequently BROKEN like this. Given that the syntax is simple BUT highly recursive why oh why can no-one write a simple proper parser, or is it the obsession with linear-ising the ENTIRE input bundle in one go that does it instead of building a naturally parsed tree. Anyone want to borrow a copy of "Understanding and Writing Compilers"....

    Pirate's as there is no Cowboy Symbol.

  9. Frumious Bandersnatch
    Linux

    another solution

    Pass your XML through a validator to make sure it's well-formed before trying to parse it. I know that's often not seen as practical, but it's in keeping with the whole XML standard thingie: if it ain't well-formed, then it shouldn't be processed, end of story. Of course, even though that's what the standard says should happen, lots of programs have problems when presented with malformed XML.

    I was wondering if an if ($child=fork) { sleep $timeout;. kill $child } pattern would work, but judging by the article the child might still never get to handle the signal. Maybe terminating with extreme prejudice (kill -9) would work. Anyone know for sure?

    Anyway, interesting article. Definitely something to be aware of when working with XS modules, though it may not always be obvious to the programmer that the library is actually calling C routines to do the work. Most of the time, probably, but maybe not all...

  10. amanfromMars Silver badge

    Registered XXXXPerts Corner

    "You know, sometimes language designers actually know what they're doing." .... By Eddie Edwards Posted Thursday 21st May 2009 15:15 GMT

    Eddie,

    That comment covered a lot of bases. ..... and a lack of leading applications for language designers who actually know what they're doing, results in Status Quo XSSes and Personal Abuses......... as if for Building a Private Pirate Empire.

    I wonder how SMART the Spooky Algorithm is on Threads of Interest/Strings of Human Readable Virtual AIMachinery C.Ode

  11. John Imrie
    Happy

    @James

    Signals in Perl because

    1) You never know when your children finish $SIG{CHLD}

    2) The system on the other end of the pipe closes unexpectedly $SIG{PIPE}

    3) Some one hits ^C $SIG{INT}

    4) Your daemons config file has just been updated $SIG{HUP}

    5) The OS asks your program to terminate $SIG{TERM}

    In all these cases you may want to do something unusual or different or as

    simple as updating a counter.

  12. Anonymous Coward
    Anonymous Coward

    Solution worse than original problem

    The problem is that signals are a dangerous old fashioned way of handling asychronous events. If you use them then the best way to handle them is with a handler that sends a message. This message can then be handled by your main loop/handler thread/handler process in a sensible way. The actual signal handler updates and modifies no data structures so it is safe. If there are multiple threads then the implementation needs to take care of synchronisation, ensuring acesses are atomic etc. but that is what all multithreaded code should do anyway. The 'solution' described is broken in more fundamental ways than the original problem in that there were at least ways to code with the original problem and it gave some sort of guarantee of responsiveness.

  13. Camilla Smythe

    Meh

    "Fuck Off I am busy."

    "Fuck Off your request is no longer valid."

    Sorted!1

  14. peter Silver badge
    Alert

    I'm with Eddie Edwards...

    Brute-forcing terminating even the most carefully-crafted object-orientated library is a recipe for a pile up. (Say, Bugs, wasn't that a realloc call you interrupted?) After that sigalarm had been raised, the only sane action would be to cross your fingers, pray hard, log the condition, print an error message, and exit swiftly.

    But yay! for perl on El Reg. More please.

  15. Craig A. Berry

    better workarounds

    So-called "safe", or more properly, deferred signals can be turned off in a less global fashion than the workarounds presented in the article by using POSIX::sigaction. If killing the interpreter is preferable to hanging, you could also use a fatal signal instead of SIGALRM as those are not deferred. These things are covered in the perlipc documentation referenced in the article but appear not to have caught the notice of the author.

  16. Craig A. Berry

    whoops

    I see the article does mention POSIX in the last paragraph, sorry.

  17. Aaron

    I'm with Peter...

    ...more Perl on the Reg please. I did not know this about signals.

  18. Anonymous Coward
    Anonymous Coward

    El Reg gets more high brow

    Soon be calling it Dr El Reg's Journal.

    Well it is an interesting find, and I am now looking at Advanced Perl Programming which does give us the 'Incidentally, signal handlers are fraught with peril.' line.

    And it blames malloc not being reentrant :) I will leave the moral to people who want to buy the book, but it is not a great moral so don't feel let down when you read it.

  19. Steve P

    $perl++

    Please.

  20. A J Stiles
    Boffin

    Hmm

    What the issue of safe vs. dangerous signal handling comes down to, is essentially the same problem as you have with electromagnetically-operated door locks. If the external power source fails, do you want the door to be locked and unopenable, or unlocked and unlockable?

    The "safe" signal behaviour of Perl 5.8 is like locking under power. If the supply is lost, you can at least get out of the building; and indeed, most of the time, that actually is what you want. But there are occasions when unlocking under power is more appropriate. For instance, storing your priceless family heirlooms in a safe that any burglar could open simply by pulling the plug might well prejudice any insurance claim.

    The situation described in the article, though, is a bit like the meter running out just after a small child has climbed into the safe.

  21. Aaron Crane (Written by Reg staff)

    Re: another solution

    @Frumious Bandersnatch:

    I was wondering if an if ($child=fork) { sleep $timeout; kill $child } pattern would work, but judging by the article the child might still never get to handle the signal

    If the buggy library is being used in the child, and the signal you send to the child isn’t being handled, then yes, that can be made to work.¹ (You probably want to use an alarm to interrupt a waitpid(), rather than just sleeping, but that’s a minor issue.) The reason it works is that you get the normal unhandled-signal behaviour (an immediate exit, for most signals), rather than needing to wait for the Perl-side handler to be invoked.

    This doesn’t deal with the underlying disadvantage of using a child process in the original querent’s situation, which is that if you’re trying to parse malformed HTML, you presumably want to actually do something with the result. Serialising a parsed DOM back to HTML which you pipe back to the parent for subsequent reparsing is… annoying.

    ¹ Technically, there’s a very small race there, regardless of what language you use, if the child process ID can be reused between your waitpid() being interrupted by the alarm and actually killing what you thought was your own child process. In practice, I think it would be overwhelmingly hard to trigger that race in the absence of a malicious attacker who isn’t subject to ulimits and who’s playing tricks like filling up the process table.

  22. Anonymous Coward
    Anonymous Coward

    Sub processes.

    "if you’re trying to parse malformed HTML, you presumably want to actually do something with the result."

    Pipes?

    Shared memory?

    The file system?

  23. Camilla Smythe

    Meh

    "If you’re trying to parse malformed HTML, you presumably want to actually do something with the result."

    Well Obviously!!!123

    Just let me get the wrong Error Code here....

    "HTTP Error 612. Your HTML is MalFormed.

    Either

    You are not connected to the interweb.

    Your browser is generating malformed HTTP requests.

    We wrote some shit HTML on our webpages.

    We recommend you re-submit your credit card details.

    Or

    Use the following Handy Wizards to set up your Home Computing Experience."

    That should be job done.

    Perhaps someone should invent Knit1, apparently PerlN is dropping too many stitches.

This topic is closed for new posts.

Other stories you might like