The use base.pm and SIGDIE problem, still not fixed in Perl 5.8.8

This weekend I’ve been playing around with a “from scratch” idea for an app/framework thingy. I’ve been assembling the pieces in my head, in notes and in reading around. While I doubt its going to become the next “IT” framework, even if it gets released to the public, I’m hoping it will give me a clearer understanding of how to make a modern web app in Perl.

On of the things I’ve been trying to do is make the core of the app fairly sensible in terms of setting up lots of stuff by default. So, for example, to default to using UTF8 encoding and Localization as well as basic stuff like signal handlers. I cheerily set up my SIGDIE handler in my initApp() method to handle stuff like checking if headers need sending before dumping the error message back to the browser and moved on.

After getting the basic thing working a little I decided to approach Localization (L10N). I’ve never tried this before and I have to be honest the suggestions on how to do this in Perl … err.. lack. I found myself hitting the same few articles time and time again. Heck even Bugzilla’s roadmap uses the same list of articles quoted else where (TJP13, Locale::Maketext::Lexicon, and the Cpanel.net roadmap for the future of Cpanel), but as with most things Perl trying and poking seems to get you somewhere.

In my case it got me more than I’d bargained for because in trying to make my app flexible the localization is optional, and more so, tried to make the use of Locale::Maketext::Lexicon optional so you can use other modules if you prefer. This meant that I was a prime candidate for the “use base” bug.

If you haven’t come across this bug then its pretty simple. There’s a bug in base.pm in which the die signal handler entry in %SIG is local()ized for part of import(). This is great because its localized and the author makes an attempt to restore it afterward to ensure it was reset correctly. Unfortunately it goes horribly wrong. And I’ve just wasted another couple of hours tracking this crap down only to discover that because this is a relatively new machine I hadn’t patched base.pm on it unlike my other servers … *grr*.

If you don’t know the bug a solution is documented on the perl5 mailing list. The code block in question is this:

 85             my $sigdie;
 86             {
 87                 local $SIG{__DIE__};
 88                 eval "require $base";
 89                 # Only ignore "Can't locate" errors from our eval require.
 90                 # Other fatal errors (syntax etc) must be reported.
 91                 die if $@ && $@ !~ /^Can't locate .*? at \(eval /;
 92                 unless (%{"$base\::"}) {
 93                     require Carp;
 94                     Carp::croak(<<ERROR);
 95 Base class package "$base" is empty.
 96     (Perhaps you need to 'use' the module which defines that package first.)
 97 ERROR
 98                 }
 99                 $sigdie = $SIG{__DIE__};
100             }
101             # Make sure a global $SIG{__DIE__} makes it out of the localization.
102             $SIG{__DIE__} = $sigdie if defined $sigdie;

The general idea here is that base.pm doesn’t want a custom SIGDIE handler interfering with what’s going on and so overrides it and tries to reset it. The bug is that this doesn’t happen. What happens is that your signal handler is always removed. Why? Well that’s because line 87 causes SIGDIE to be defined, but to be defined with no value. It seems that this is the default for signal handlers, even though it doesn’t appear to happen for other variables, including hashes:

#!/usr/bin/perl
use strict;
use warnings;
use vars qw(%A);

print "\$A{B} def: ", defined($A{B}) ? 'Yes' : 'No', "\n";
$A{B} = 1;
print "\$A{B} def: ", defined($A{B}) ? 'Yes' : 'No', "\n";
local($A{B});
print "\$A{B} def: ", defined($A{B}) ? 'Yes' : 'No', "\n";

print "\$SIG{__DIE__} def: ", defined($SIG{__DIE__}) ? 'Yes' : 'No', "\n";
$SIG{__DIE__} = 'IGNORE';
print "\$SIG{__DIE__} def: ", defined($SIG{__DIE__}) ? 'Yes' : 'No', "\n";
local($SIG{__DIE__});
print "\$SIG{__DIE__} def: ", defined($SIG{__DIE__}) ? 'Yes' : 'No', "\n";

This outputs the following. Can you spot the problem?

$A{B} def: No
$A{B} def: Yes
$A{B} def: No
$SIG{__DIE__} def: No
$SIG{__DIE__} def: Yes
$SIG{__DIE__} def: Yes

That’s right. In a normal hash once we localize a hash variable then we end up with it not being defined, irregardless of whether or not the original var was defined. This doesn’t hold true for %SIG. In the case of %SIG localizing it causes the elements to be defined. ALWAYS.

Normally this isn’t a problem because you don’t use these, and besides which the value is defined but zero. But in the way this is handled in base.pm, its a problem because of the strange way that SIGDIE is restored from the var $sigdie, but $sigdie is assigned to after the call to local():

 99                 $sigdie = $SIG{__DIE__};
100             }
101             # Make sure a global $SIG{__DIE__} makes it out of the localization.
102             $SIG{__DIE__} = $sigdie if defined $sigdie;

Thanks to above quirk with %SIG and local in this situation $sigdie (line 99) is ALWAYS defined, but because $sigdie is assigned to after local() is used, $sigdie doesn’t contain your signal handler, but instead contains “”. So we hit line 102 where the block is conditional on $sigdie being defined, which it always is, remember, and thus the code always assigns “” to $SIG{__DIE__} after loading the given module. As line 102 is outside of the block in which local() is used at this point %SIG is the actual %SIG (the global copy if you prefer). Therefore this assignation has the effect of wiping out any signal handler you may have installed despite the fact that it is trying to achieve exactly the opposite and restore the original signal handler.

The solution mentioned above is to use || undef on line 99. This has the effect of assigning undef to $sigdie because the signal handler is “”, or false, thus line 102 doesn’t pass the conditional and install this “” as the SIGDIE handler. It works, but I have to ask, why bother? It seems like the better way is to simply remove all of the $sigdie stuff and localize %SIG resetting it to its default, and then let the code exit the block. When it does the localized version of %SIG is removed and there’s no risk of clobbering an existing handler.

An alternative, if you must use $sigdie, is to assign the current SIGDIE handler as you’re declaring it, thus:

 85             my $sigdie = $SIG{__DIE__};
 86             {
 87                 local $SIG{__DIE__};

Then remove line 99 where the value is messed up:

 99                 $sigdie = $SIG{__DIE__};

Having just checked for updated modules the other day, I have to say, I’m very surprised that there isn’t a patched version of this which has been sent out to versions of Perl with broken versions. As I understand it 5.10 doesn’t suffer this problem, but since the stable version is still considered to be 5.8.8 it seems like it should be updated too. It would save people like me wasting a few hours trying to track down what’s going on with their code….

Colin.

Advertisements

Firebug’s Hidden Gems

Like many people who develop sites these days I find that Firebug is an indispensable part of my toolkit. It has evolved from “yet another web developer extension” into a tool which is an essential part of any developer’s toolkit – at least if they’re using anything but plain HTML. Its facilities for observing, testing and tweaking what’s going on in your web application are countless and as far as I’m aware, without compare.

If you haven’t tried it then you’re severely handicapping yourself so I would recommend that you install it and start playing. If you already have it, then you’ll know what I mean – the moment you end up on a computer without it, you feel like you need it installed. But even having used it for a good while (years?) now, I’m constantly amazed by the attention to detail the developers and contributors have paid.

For example I was just poking around with a test site trying out some CSS and discovered something new tonight. Like me you’re probably used to opening up Firebug to tweak the CSS styles to see how things will look without needing to reload the page. I should probably point out that if you’re not used to Firebug and you think it is just for looking at things, then you’re missing a good 50% of the picture… In fact lets try it.

Select an element on screen using the “Inspect” button in the HTML panel. You’ll see the DOM tree open up to that element. Want to try out a change, say a different background color? Select the “BODY” element and then right-click. Lots of options huh? Select “New Attribute” and you’ll get to add a new attribute to the element. So here’s a quick way of testing a new background, border, etc. – add the attribute “style”. Here’s a perfect example of the attention being paid. Instead of typing it all it, you enter only the name, and then hit “tab” and you’ll move on to entering the attribute’s value. You don’t need to worry about the equals symbol or quotes because FB fixes that for you, you just enter the value you want to use.

The alternative to editing this inline is to use the Sub-Panels on the right of the HTML Panel. When you view the DOM tree you’ll see that the sub-panels on the right update. They show the Style, Layout and DOM for the selected element. Here again is an example of the detail. The Style sub-panel shows by default the CSS rules applicable to this element. It strikes through rules which have been superseded as well as the source file and line number. This is extremely handy for working out why your DIV tag has a bright blue background instead of bright red. But by checking an option in the “Options” menu you can see the additional rules set by Firefox (this may be a 1.3 track setting) and view the computed style rules.

Okay so yes, this was available in the DOM inspector, but the packaging is nicer, and it is combined with the particularly useful Layout tab which allows you to see, and modify, the settings for the layout characteristics – border, padding, margin, height, width and top/left offsets. If you haven’t discovered it yet (or read the manual, may be its all in there?), then you can click on any of the numbers in the Layout tab and edit them. Did you also discover (read about?) how when you hit tab you move on to the next number in the square? And how you can tab all the way around from offset to margin to border to padding in a loop? Oh and of course how if you shift-tab you tab backwards in reverse order? Yeah, that’s the kind of attention that I’m talking about.

Well if you found all of that and think that’s cool then there’s the surprise I found tonight back on the “Style” tab. I knew that instead of editing the style attribute you could also edit the styles for an element from with in the Style tab – if you didn’t then select your element and right click in the style sub-panel and you’ll see a host of options for editing the style, adding new properties, editing the existing properties etc.

So here’s the cool bit… Can’t remember what the CSS property is called? No problem they’ve thought of that too. Type whatever you can think it starts with and hit up/down. You’ll scroll through all of the properties that FB knows about. This includes all of the new CSS3 and -moz extensions that FFx supports. If that wasn’t handy enough hit tab to go on to editing the value… and you can do the same thing.

This was the “easter egg” I found tonight, having FB be intelligent enough that I was able to scroll through appropriate values for an attribute (handy with some of the obscure/new -moz… attributes).

For example; If your value is numeric then up will increment and down will decrement, seems reasonable. But if it is a known CSS attribute then you’ll be stepped through a list of possible options. For example, can’t remember what border styles are supported? No problem add an attribute “border-style” tab into the value field and scroll through them all with up/down. Can’t remember the (256?) named web colours? Again, if its a recognized colour attribute then you can scroll up/down through a list of known colours.

If that isn’t good enough try it with a combination attribute such as “border”. Try editing a standard CSS value like “border: 1px solid black”. If you edit this value and hit up/down then the border width will increment/decrement. Okay so you’re not impressed because its only doing it because it starts with a number. So double-click on the border-style (in this case “solid”) and yeah, you can scroll through a list of known border-styles. When you’ve found something you like you can repeat this with colour attribute. If the auto-complete of the names, the tab/shift-tab support wasn’t enough then this while not commonly used, just makes it a much nicer experience.

A huge thank you to Joe Hewitt, John J Barton, and everyone else for this amazing extension.

Colin.