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.
Perl 6 and the “Vaporware” Label
October 25, 2008 1 Comment
Perusing Perlbuzz, as I tend to do on a rainy saturday afternoon, I was intrigued by Andy Lester’s latest post “Perl 6 isn’t exactly vaporware” in which he comments on the vaporware label that is often used around Perl 6.
I must say that I found it quite interesting to see Andy calling for an early adopter’s release of Rakudo because as he notes,
A while ago I moved this blog to my own site on perladmin.oreally.co.uk. Posting about the move I tried to explain what I was hoping to achieve with the blog – to do my part in building attention to Perl. As part of this I made the observation that I believe one of the reasons that industry interest has turned away from Perl is because, in part, that we keep being promised that P6 is coming, but there is no usable implementation.
The comments I received about the post concentrated only on this part of the post and it was noted that by “bashing” Perl 6 in this way I was only adding to the problem. I suppose that a comment such as this from someone who claims to support Perl could be construed as in-fighting, but this was not the intent of my comment. Rather I was pointing out what I believe to be a very simple flaw in our collective front; that by talking about how great this language will be, but never having anything substantial in circulation, we look bad. Think about the disparaging comments that circulated about Microsoft and its repeated resetting of release dates for various versions of Windows and Office.
I’m not, of course, knocking Perl 6, its hard working designers or developers, or any such thing. Indeed quite the opposite I’m trying to say how I think Perl can be improved after all more interest in Perl means more, and sustained, interesting in Perl 6. Having tried to explain my thoughts, I was amused to read Andy’s post stating exactly the same point that I was trying to get across – that I think Perl is already the best solution to many problems, and that the descriptions we get of Perl 6 make me curious and want to use it, but that I cannot because it isn’t yet available in a form which is stable enough for general use.
Of further interest to those in the Perl community who have a similar view to mine are some of the comments posted in response to Andy’s posting. Indeed the second commenter asks
This is a rather disconcerting comment is further strengthened as the author makes mention of some of the issues and while supporters can easily wave off most of them the comment author provides one example as:
If something like this doesn’t work then it seems to be a long way from being usable. Now your definition of usable may be different from mine, but in my definition it means I can write my apps using it. It doesn’t, of course, mean that I don’t want/like Perl 6 or its designers or developers. I don’t have anything against it, I just don’t think that it is ready and both Andy’s post and the associated comments appear to emphasize that point.
So while it is being developed IMO that the rest of us can help rebuild the love for Perl by concentrating on what Perl can do right now and just accept that Perl 6 isn’t going to be in a cinema near you soon. So lets go Team Perl! :)
Colin.
Incidentally, this is being posted here rather than on my own site, perladmin.oreally.co.uk, because its currently being moved to a new server. It was previously hosted on a box thrown together from junk in 1999. As you might imagine it was slow when receiving any real level of traffic but it was finally withdrawn from service due to its power usage. As a sign of the times, RedBus – the London, England, data center in which the box was hosted – has recently changed its billing calculations to include power consumption and as such a 9 year old box was not a good thing to have around for free and so it needed to be pulled. As a comparison cabinet space and bandwidth, the usual billing metric for data centers, is all but free. The migration has yet to be completed due to various complicating factors. Until then I’ll likely be posting through wordpress again.
Filed under advocacy, Perl Tagged with commentary, Perl