File Coverage

blib/lib/Signals/XSIG.pm
Criterion Covered Total %
statement 226 259 87.2
branch 55 82 67.0
condition 33 68 48.5
subroutine 53 63 84.1
pod 0 2 0.0
total 367 474 77.4


line stmt bran cond sub pod time code
1             package Signals::XSIG;
2              
3             ## no critic (RequireLocalizedPunctuationVars, ProhibitAutomaticExport)
4              
5 13     13   54865 use Signals::XSIG::Default;
  13         32  
  13         62  
6 13     13   76 use Carp;
  13         24  
  13         592  
7 13     13   56 use Config;
  13         18  
  13         345  
8 13     13   47 use Exporter;
  13         18  
  13         255  
9 13     13   4811 use Symbol;
  13         7891  
  13         602  
10 13     13   109 use warnings;
  13         21  
  13         305  
11 13     13   45 use strict;
  13         128  
  13         2015  
12             require Signals::XSIG::Meta;
13              
14             our @ISA = qw(Exporter);
15             our @EXPORT = qw(%XSIG);
16             our @EXPORT_OK = qw(%DEFAULT_BEHAVIOR);
17              
18             our %DEFAULT_BEHAVIOR;
19             *DEFAULT_BEHAVIOR = \%Signals::XSIG::Default::DEFAULT_BEHAVIOR;
20              
21             our $VERSION = '1.00';
22             our (%XSIG);
23             our (%OSIG, %ZSIG, %SIGTABLE, %alias, $_REFRESH);
24             my (%OOSIG, %YSIG, $_DISABLE_WARNINGS);
25             our $_INITIALIZED = 0;
26              
27             # singleton tied hash classes
28             our $SIGTIE = bless {}, 'Signals::XSIG::TieSIG';
29             our $XSIGTIE = bless {}, 'Signals::XSIG::TieXSIG';
30              
31             ###
32             # to be removed when we get a handle on intermittent t/02 failures
33             our ($XDEBUG,$XN,@XLOG) = ($ENV{XSIGDEBUG},1001);
34             sub XLOG{
35 13     13   64 no warnings "uninitialized";
  13         25  
  13         2041  
36 30     30 0 87 push @XLOG,($XN++)." @_";
37 30 50       45 XLOG_DUMP() if $XDEBUG>1
38             }
39 0     0 0 0 sub XLOG_DUMP{my $r=join("\n",@XLOG);@XLOG=();Test::More::diag($r)}
  0         0  
  0         0  
40             ###
41              
42              
43             BEGIN {
44             # how to detect global destruction. Idea from Devel::GlobalDestruction 0.13
45 13 50   13   66 if (defined ${^GLOBAL_PHASE}) {
46 13 0   0   879 eval 'sub __inGD() {${^GLOBAL_PHASE} eq "DESTRUCT" && __END()}; 1';
  0         0  
47             } else {
48 0         0 require B;
49 0         0 eval 'sub __inGD() { ${B::main_cv()}==0 && __END();}; 1';
50             }
51             }
52              
53             sub import {
54 19     19   8085 Signals::XSIG->export_to_level(1, @_);
55 19         60 _init();
56             }
57              
58             sub unimport {
59 1     1   202 untie %main::SIG;
60             }
61              
62             sub _init {
63 19 100   19   153 return if $_INITIALIZED;
64 14         22 $_REFRESH = 0;
65 14 50 33     447 if ($ENV{XSIG_56} || $Config{PERL_VERSION} <= 6) {
66 0         0 print STDERR "Using Signals::XSIG::Meta56 to manage multi-handlers\n";
67 0         0 require Signals::XSIG::Meta56;
68             }
69              
70 14         36 my @z = ();
71 14         1171 my @num = split ' ', $Config{sig_num};
72 14         178 my @name = split ' ', $Config{sig_name};
73 14         54 for (my $i=0; $i<@name; $i++) {
74 966         923 my $signo = $num[$i];
75 966         811 my $signame = $name[$i];
76 966   66     2644 $SIGTABLE{$signo} ||= $signame;
77 966         2124 $SIGTABLE{'SIG' . $signame} = $SIGTABLE{$signame} = $SIGTABLE{$signo};
78 966 100       1300 if (defined $z[$signo]) {
79 56         79 $alias{$signame} = $z[$signo];
80 56         75 $alias{$z[$signo]} = $signame;
81 56         107 $alias{"pk:$z[$signo]"} = 1;
82 56         107 $alias{"sub:$signame"} = 1;
83             }
84 966         1691 $z[$signo] = $signame;
85             }
86 14         30 $SIGTABLE{'__WARN__'} = '__WARN__';
87 14         23 $SIGTABLE{'__DIE__'} = '__DIE__';
88              
89 14         55 *OSIG = \%main::SIG;
90 14         2038 %OOSIG = %OSIG;
91              
92 14         68 foreach my $sig ('__WARN__', '__DIE__', @name) {
93 994 100       1649 next if $SIGTABLE{$sig} ne $sig;
94 938         1356 $ZSIG{$sig} = Signals::XSIG->_new($sig, $OSIG{$sig});
95             }
96 14         30 *main::SIG = \%YSIG;
97 14         47 tie %SIG, 'Signals::XSIG::TieSIG';
98 14         43 tie %XSIG, 'Signals::XSIG::TieXSIG';
99 14         22 $_REFRESH = 1;
100 14         141 $_->_refresh foreach values %ZSIG;
101 14         595 return ++$_INITIALIZED;
102             }
103              
104 13     13   6023051 END { &__END; }
105              
106             sub __END {
107 13     13   75 no warnings 'redefine';
  13         17  
  13         1286  
108 13     13   307 *__inGD = sub () { 1 };
109             }
110              
111             sub Signals::XSIG::_sigaction { # function, not method
112 32     32   71 my ($signal, @args) = @_;
113 32         37 foreach my $handler (@{$ZSIG{$signal}->{xh}}) {
  32         65  
114             # print STDERR "_sigaction($signal) => $handler\n";
115 140 100 33     462 if ($handler eq 'DEFAULT') {
    50          
116 4         11 Signals::XSIG::Default::perform_default_behavior(
117             $signal, @args);
118             } elsif ($signal ne '__WARN__' && $signal ne '__DIE__') {
119 13     13   66 no strict 'refs';
  13         26  
  13         529  
120 136         206 $handler->($signal, @args);
121             } else {
122 13     13   56 no warnings 'uninitialized';
  13         18  
  13         351  
123 13     13   52 no strict 'refs';
  13         15  
  13         5367  
124 0         0 print STDERR "Calling $signal signal handler $handler \@args=@args\n";
125 0         0 $handler->($signal, @args);
126             }
127             }
128             }
129              
130              
131             # convert a signal name to its canonical name. If not disabled,
132             # warn if the input is not a valid signal name.
133             # TERM => TERM
134             # CLD => CHLD
135             # OOK => warning
136             sub _resolve_signal {
137 2535     2535   3095 my ($sig, $DISABLE_WARNINGS) = @_;
138 2535   66     6305 $DISABLE_WARNINGS ||= $_DISABLE_WARNINGS;
139 2535         3599 $sig = $SIGTABLE{uc $sig};
140 2535 100       3301 if (defined $sig) {
141 2503         2453 $_[0] = $sig; ## no critic (Unpacking)
142 2503         3757 return 1;
143             }
144 32 50       52 return 1 if !$_INITIALIZED;
145              
146             # signal could not be resolved -- issue warning and return false
147 32 100       74 unless ($DISABLE_WARNINGS) {
148 10 50 33     1450 if (defined($sig) && $sig =~ /\d/ && $sig !~ /\D/) {
    50 33        
149 0         0 carp "Invalid signal number $sig.\n";
150             } elsif (warnings::enabled('signal')) {
151 0         0 Carp::cluck "Invalid signal name $sig.\n";
152             }
153             }
154 32         55 return;
155             }
156              
157              
158             # in %SIG and %XSIG assignments, string values are qualified to the
159             # 'main' package, unqualified *glob values are qualified to the
160             # calling package.
161             sub _qualify_handler {
162 1971     1971   2159 my $handler = shift(@_);
163              
164 1971 100 100     4955 if (!defined($handler)
      100        
      100        
165             || $handler eq ''
166             || $handler eq 'IGNORE'
167             || $handler eq 'DEFAULT') {
168 1447         2934 return $handler;
169             }
170              
171 524 100       1166 if (substr($handler,0,1) eq '*') {
172 18         27 my $n = 0;
173 18         35 my $package = caller;
174 18   66     137 while (defined($package) && $package =~ /^Signals::XSIG/) {
175 41         149 $package = caller(++$n);
176             }
177 18   50     63 $handler = Symbol::qualify($handler, $package || 'main');
178             } else {
179 506         1073 $handler = Symbol::qualify($handler, 'main');
180             }
181 524         3145 return $handler;
182             }
183              
184             #####################################################
185             #
186             # Signals::XSIG::TieSIG
187             #
188             # Only for tie-ing %SIG.
189             # Associates $SIG{key} with $XSIG{$sig}[0]
190             #
191              
192             sub Signals::XSIG::TieSIG::TIEHASH {
193 14 50   14   59 $XDEBUG && XLOG("TieSIG::TIE caller=",caller);
194 14         43 return $SIGTIE;
195             }
196              
197             # called when we say $x = $SIG{signal}
198             sub Signals::XSIG::TieSIG::FETCH {
199 1364     1364   652507 my ($self,$key) = @_;
200              
201             # track function calls to diagnose test failures
202 1364 100 66     2406 $XDEBUG && $key ne '__DIE__'
203             && XLOG("FETCH key=$key caller=",caller);
204 1364 100       1998 if (_resolve_signal($key)) {
205 1357         2741 my $old = $ZSIG{$key}->_fetch(0);
206 1357 100 66     2122 $XDEBUG && $key ne '__DIE__' && XLOG(" result(1)=",$old);
207 1357         4134 return $old;
208             } else {
209 13     13   75 no warnings 'uninitialized';
  13         27  
  13         1250  
210 7         18 my $r = $OSIG{$key};
211 7 50 33     12 $XDEBUG && $key ne '__DIE__' && XLOG(" result(2)=$r");
212 7         36 return $r;
213             }
214             }
215              
216             # called when we say $SIG{signal} = handler
217             sub Signals::XSIG::TieSIG::STORE {
218 870     870   9230 my ($self,$key,$value) = @_;
219 13     13   62 no warnings 'uninitialized';
  13         21  
  13         1098  
220 870 100 66     1747 $XDEBUG && $key ne '__DIE__'
221             && XLOG("STORE($key,$value) caller=",caller);
222 870 100       1163 if (_resolve_signal($key)) {
223 868         1977 return $ZSIG{$key}->_store(0, $value);
224             } else {
225 2         4 my $old;
226 2 50       4 $XDEBUG && XLOG(" key $key not resolved");
227 13     13   78 no warnings 'signal'; ## no critic (NoWarnings)
  13         26  
  13         1823  
228 2         5 $old = $OSIG{$key};
229 2         13 $OSIG{$key} = $value;
230 2         7 return $old;
231             }
232             }
233              
234             # called when we say delete $SIG{signal}
235             sub Signals::XSIG::TieSIG::DELETE {
236 29     29   114 my ($self,$key) = @_;
237 29 50 33     106 $XDEBUG && $key ne '__DIE__'
238             && XLOG("DELETE key=$key caller=",caller);
239 29 100       79 if (_resolve_signal($key)) {
240 28         107 my $old = $ZSIG{$key}->_store(0, undef);
241 28 50       74 $XDEBUG && XLOG(" DELETE result(1)=$old");
242 28         875 return $old;
243             } else {
244 1         4 my $old = $self->FETCH($key);
245 13     13   73 no warnings 'uninitialized';
  13         15  
  13         5077  
246 1 50       3 $XDEBUG && XLOG(" DELETE result(2)=$old");
247 1         44 $OSIG{$key} = undef;
248 1         5 delete $OSIG{$key};
249 1         4 return $old;
250             }
251             }
252              
253             # called when we say %SIG = ();
254             sub Signals::XSIG::TieSIG::CLEAR {
255 2     2   4 my ($self) = @_;
256 2 50 33     14 $XDEBUG && $^O ne "MSWin32" && do {
257 2         6 XLOG("CLEAR caller=",caller);
258 2         2 XLOG(" CLEAR init USR1=",@{$ZSIG{USR1}{handlers}});
  2         5  
259 2         2 XLOG(" CLEAR init USR2=",@{$ZSIG{USR2}{handlers}});
  2         4  
260             };
261              
262 2         69 my @sigs = reverse sort keys %ZSIG;
263 2 50       19 $XDEBUG && XLOG(" \@sigs = qw(@sigs);");
264 2         4 for (@sigs) {
265 134         224 $ZSIG{$_} = Signals::XSIG->_new($_);
266             }
267              
268 2 50 33     10 $XDEBUG && $^O ne 'MSWin32' && do {
269 2         3 XLOG(" CLEAR final USR1=",@{$ZSIG{USR1}{handlers}});
  2         15  
270 2         2 XLOG(" CLEAR final USR2=",@{$ZSIG{USR2}{handlers}});
  2         5  
271             };
272 2         12 return;
273             }
274              
275             # called when we say exists $SIG{signal}
276             sub Signals::XSIG::TieSIG::EXISTS {
277 488     488   108168101 my ($self,$key) = @_;
278 488 50 33     904 $XDEBUG && $key ne '__DIE__'
279             && XLOG("EXISTS key=$key caller=",caller);
280 488         1944 return exists $OSIG{$key};
281             }
282              
283             sub Signals::XSIG::TieSIG::FIRSTKEY {
284 4     4   716 my ($self) = @_;
285 4 50       10 $XDEBUG && XLOG("FIRSTKEY caller=",caller);
286 4         9 my $a = keys %OSIG;
287 4 50       9 $XDEBUG && XLOG(" FIRSTKEY result=$a");
288 4         16 return each %OSIG;
289             }
290              
291             sub Signals::XSIG::TieSIG::NEXTKEY {
292 280     280   302 my ($self, $lastkey) = @_;
293 280 50       339 $XDEBUG && XLOG("NEXTKEY lastkey=$lastkey caller=",caller);
294 280         516 return each %OSIG;
295             }
296              
297             # invoked with untie %SIG
298             sub Signals::XSIG::TieSIG::UNTIE {
299 13     13   66 no warnings 'uninitialized';
  13         19  
  13         1850  
300 1 50 33 1   4 $XDEBUG && !__inGD() && XLOG("UNTIE caller=",caller);
301 1         2 *main::SIG = \%Signals::XSIG::OSIG;
302 1         1 *OSIG = \%YSIG;
303 1         411 %main::SIG = %OOSIG;
304 1         4 $_INITIALIZED = 0;
305 1         3 return;
306             }
307              
308             ############################################################
309             #
310             # Signals::XSIG::TieArray
311             #
312             # Creates association between @{$XSIG{signal}} / $XSIG{signal}[index]
313             # and $ZSIG{signal}
314             #
315              
316             sub Signals::XSIG::TieArray::TIEARRAY {
317 1233     1233   1659 my ($class, $signal) = @_;
318 1233         2954 return bless {key => $signal}, 'Signals::XSIG::TieArray';
319             }
320              
321             # Those Perl guys thought of everything.
322             {
323 13     13   87 no warnings 'once';
  13         21  
  13         11013  
324             $Signals::XSIG::TieArray::NEGATIVE_INDICES = 1;
325             }
326              
327             # called with $x = $XSIG{signal}[index]
328             sub Signals::XSIG::TieArray::FETCH {
329 141     141   208 my ($self, $index) = @_;
330 141         319 return $ZSIG{$self->{key}}->_fetch($index);
331             }
332              
333              
334             # called with $XSIG{signal}[index] = handler
335             sub Signals::XSIG::TieArray::STORE {
336 50     50   79 my ($self, $index, $handler) = @_;
337 50         118 return $ZSIG{$self->{key}}->_store($index, $handler);
338             }
339              
340             sub Signals::XSIG::TieArray::handlers {
341 0     0   0 my $self = shift(@_);
342 0         0 return @{$self->{handlers}};
  0         0  
343             }
344              
345             sub Signals::XSIG::TieArray::FETCHSIZE {
346 0     0   0 my ($self) = @_;
347 0         0 return $ZSIG{$self->{key}}->_size;
348             }
349              
350       0     sub Signals::XSIG::TieArray::STORESIZE { }
351              
352       2     sub Signals::XSIG::TieArray::EXTEND { }
353              
354             # called with exists $XSIG{signal}[index]
355             sub Signals::XSIG::TieArray::EXISTS {
356 0     0   0 my ($self, $index) = @_;
357 0         0 my $zsig = $ZSIG{$self->{key}};
358 0 0       0 return if $index < $zsig->{start};
359 0         0 return exists $zsig->{handlers}[$index - $zsig->{start}];
360             }
361              
362              
363             # called with delete $XSIG{signal}[index]
364             sub Signals::XSIG::TieArray::DELETE {
365 4     4   9 my ($self, $index) = @_;
366 4         16 return $ZSIG{$self->{key}}->_store($index, undef);
367             }
368              
369             # called with $XSIG{signal} = []
370             # @{$XSIG{signal}} = ()
371             sub Signals::XSIG::TieArray::CLEAR {
372 2     2   6 my ($self) = @_;
373 2         12 $ZSIG{$self->{key}} = Signals::XSIG->_new($self->{key});
374 2         10 return;
375             }
376              
377              
378             # called with unshift @{$XSIG{signal}}, @list
379             sub Signals::XSIG::TieArray::UNSHIFT {
380 4     4   11 my ($self, @list) = @_;
381 4         12 return $ZSIG{$self->{key}}->_unshift(@list);
382             }
383              
384              
385             # called with pop @{$XSIG{signal}}
386             sub Signals::XSIG::TieArray::POP {
387 5     5   8 my ($self) = @_;
388 5         15 return $ZSIG{$self->{key}}->_pop;
389             }
390              
391              
392             # called with shift @{$XSIG{signal}}
393             sub Signals::XSIG::TieArray::SHIFT {
394 4     4   7 my $self = shift(@_);
395 4         12 return $ZSIG{$self->{key}}->_shift;
396             }
397              
398              
399             # called with push @{$XSIG{signal}}, @list
400             sub Signals::XSIG::TieArray::PUSH {
401 7     7   15 my ($self, @list) = @_;
402 7         22 return $ZSIG{$self->{key}}->_push(@list);
403             }
404              
405       0     sub Signals::XSIG::TieArray::SPLICE { } # TODO
406              
407             ##################################################################
408             #
409             # Signals::XSIG::TieXSIG
410             #
411             # $XSIG{sig} must produce a tied array that can be used in these ways:
412             #
413             # $aref = $XSIG{signal}
414             # $XSIG{signal} = $aref
415             # @{$XSIG{signal}} = @list
416             # $h = $XSIG{signal}[index]
417             # $XSIG{signal}[index] = $h
418             # push|unshift @{$XSIG{signal}}, @list
419             # $h = pop|shift @{$XSIG{signal}}
420             #
421             #
422             # Only for tie-ing %XSIG.
423             # adds behavior to %XSIG hash so we can make assignments to
424             # $XSIG{sig} so that @{$XSIG{$sig}} is always a
425             # Signals::XSIG::TieArray
426             #
427              
428             sub Signals::XSIG::TieXSIG::TIEHASH {
429 14     14   25 return $XSIGTIE;
430             }
431              
432             # $aref = $XSIG{signal}
433              
434             sub Signals::XSIG::TieXSIG::FETCH {
435 240     240   15767 my ($self,$key) = @_;
436 240         413 _resolve_signal($key,1);
437 240 100       854 return $ZSIG{$key} ? $ZSIG{$key}{ta} : [];
438             }
439              
440             # called with $XSIG{signal} = handler, $XSIG{signal} = [handlers]
441             sub Signals::XSIG::TieXSIG::STORE {
442 29     29   4030 my ($self, $key, $value) = @_;
443             # print STDERR "\n\aTieXSIG::STORE($key)\n\n";
444 29         67 _resolve_signal($key,1);
445 29 100       60 return unless $ZSIG{$key};
446 25 50       55 if (ref $value ne 'ARRAY') {
447 0         0 $value = [ $value ];
448             }
449 25         37 my $old = $ZSIG{$key}->{handlers};
450 25         73 $ZSIG{$key} = Signals::XSIG->_new($key, @$value);
451 25         87 return $old;
452             }
453              
454             sub Signals::XSIG::TieXSIG::DELETE {
455 0     0   0 my ($self, $key) = @_;
456 0         0 _resolve_signal($key,1);
457 0   0     0 my $old = $ZSIG{$key} && $ZSIG{$key}->{handlers};
458 0   0     0 $ZSIG{$key} &&= Signals::XSIG->_new($key);
459 0         0 return $old;
460             }
461              
462             sub Signals::XSIG::TieXSIG::CLEAR {
463 2     2   137 my ($self) = @_;
464 2         33 my @names = keys %ZSIG;
465 2         5 for my $key (@names) {
466 134         213 $ZSIG{$key} = Signals::XSIG->_new($key);
467             }
468 2         14 return;
469             }
470              
471             sub Signals::XSIG::TieXSIG::EXISTS {
472 3     3   11 my ($self,$key) = @_;
473 3         8 _resolve_signal($key,1);
474 3         10 return exists $ZSIG{$key};
475             }
476              
477             sub Signals::XSIG::TieXSIG::FIRSTKEY {
478 0     0   0 my ($self) = @_;
479 0         0 my $a = keys %ZSIG;
480 0         0 return each %ZSIG;
481             }
482              
483             sub Signals::XSIG::TieXSIG::NEXTKEY {
484 0     0   0 my ($self, $lastkey) = @_;
485 0         0 return each %ZSIG
486             }
487              
488              
489             # Signals::XSIG object implementation, all with 'private' functions
490             # Elements of %ZSIG are objects of this type
491              
492             sub _new {
493 1233     1233   2012 my ($___pkg, $sig, @handlers) = @_;
494 1233 50 33     6094 if ($ENV{XSIG_56} || $Config{PERL_VERSION} <= 6) {
495 0         0 return Signals::XSIG::Meta56->_new($sig, @handlers);
496             } else {
497 1233         2422 return Signals::XSIG::Meta->_new($sig, @handlers);
498             }
499             }
500              
501              
502             1;
503              
504             __END__