File Coverage

blib/lib/MCE/Signal.pm
Criterion Covered Total %
statement 89 172 51.7
branch 42 144 29.1
condition 25 113 22.1
subroutine 17 25 68.0
pod 3 3 100.0
total 176 457 38.5


line stmt bran cond sub pod time code
1             ###############################################################################
2             ## ----------------------------------------------------------------------------
3             ## Temporary directory creation/cleanup and signal handling.
4             ##
5             ###############################################################################
6              
7             package MCE::Signal;
8              
9 97     97   347390 use strict;
  97         200  
  97         2489  
10 97     97   378 use warnings;
  97         177  
  97         2408  
11              
12 97     97   378 no warnings qw( threads recursion uninitialized once );
  97         148  
  97         8935  
13              
14             our $VERSION = '1.887';
15              
16             ## no critic (BuiltinFunctions::ProhibitStringyEval)
17              
18             our ($display_die_with_localtime, $display_warn_with_localtime);
19             our ($main_proc_id, $prog_name, $tmp_dir);
20              
21             tie $tmp_dir, 'MCE::Signal::_tmpdir';
22              
23 97     97   582 use Carp ();
  97         186  
  97         7986  
24              
25             BEGIN {
26 97     97   428 $main_proc_id = $$;
27 97         177 $prog_name = $0;
28 97         527 $prog_name =~ s{^.*[\\/]}{}g;
29 97 50 33     1210 $prog_name = 'perl' if ($prog_name eq '-e' || $prog_name eq '-');
30              
31 97         2313 return;
32             }
33              
34 97     97   507 use base qw( Exporter );
  97         172  
  97         9234  
35 97     97   570 use Time::HiRes ();
  97         144  
  97         99333  
36              
37             our @EXPORT_OK = qw( $tmp_dir sys_cmd stop_and_exit );
38             our %EXPORT_TAGS = (
39             all => \@EXPORT_OK,
40             tmp_dir => [ qw( $tmp_dir ) ]
41             );
42              
43             END {
44 97 50 66 97   7443 MCE::Signal->stop_and_exit($?)
      66        
45             if ($$ == $main_proc_id && !$MCE::Signal::KILLED && !$MCE::Signal::STOPPED);
46             }
47              
48             ###############################################################################
49             ## ----------------------------------------------------------------------------
50             ## Process import, export, & module arguments.
51             ##
52             ###############################################################################
53              
54 0     0   0 sub _croak { $\ = undef; goto &Carp::croak }
  0         0  
55 0     0   0 sub _usage { _croak "MCE::Signal error: ($_[0]) is not a valid option" }
56 1     1   4 sub _flag { 1 }
57              
58             my $_is_MSWin32 = ($^O eq 'MSWin32') ? 1 : 0;
59             my $_keep_tmp_dir = 0;
60             my $_use_dev_shm = 0;
61             my $_no_kill9 = 0;
62             my $_imported;
63              
64             sub import {
65 4     4   35 my $_class = shift;
66 4 50       13 return if $_imported++;
67              
68 4         9 my ($_no_setpgrp, $_no_sigmsg, $_setpgrp, @_export_args) = (0, 0, 0);
69              
70 4         11 while (my $_arg = shift) {
71 7 50 0     14 $_setpgrp = _flag() and next if ($_arg eq '-setpgrp');
72 7 50 0     12 $_keep_tmp_dir = _flag() and next if ($_arg eq '-keep_tmp_dir');
73 7 100 50     13 $_use_dev_shm = _flag() and next if ($_arg eq '-use_dev_shm');
74 6 50 0     10 $_no_kill9 = _flag() and next if ($_arg eq '-no_kill9');
75              
76             # deprecated options for backwards compatibility
77 6 50 0     12 $_no_setpgrp = _flag() and next if ($_arg eq '-no_setpgrp');
78 6 50 0     10 $_no_sigmsg = _flag() and next if ($_arg eq '-no_sigmsg');
79              
80 6 50       14 _usage($_arg) if ($_arg =~ /^-/);
81              
82 6         44 push @_export_args, $_arg;
83             }
84              
85 4         8 local $Exporter::ExportLevel = 1;
86 4         367 Exporter::import($_class, @_export_args);
87              
88             ## Sets the current process group for the current process.
89 4 50 33     18 setpgrp(0,0) if ($_setpgrp == 1 && !$_is_MSWin32);
90              
91             ## Make tmp_dir if caller requested it.
92 4 100 100     39 _make_tmpdir() if ($_use_dev_shm || grep /tmp_dir/, @_export_args);
93              
94 4         49 return;
95             }
96              
97             ###############################################################################
98             ## ----------------------------------------------------------------------------
99             ## Configure signal handling.
100             ##
101             ###############################################################################
102              
103             ## Set traps to catch signals.
104             if ( !$_is_MSWin32 ) {
105             $SIG{ABRT} = \&stop_and_exit; # UNIX SIG 6
106             $SIG{HUP} = \&stop_and_exit; # UNIX SIG 1
107             $SIG{INT} = \&stop_and_exit; # UNIX SIG 2
108             $SIG{PIPE} = \&stop_and_exit; # UNIX SIG 13
109             $SIG{QUIT} = \&stop_and_exit; # UNIX SIG 3
110             $SIG{TERM} = \&stop_and_exit; # UNIX SIG 15
111              
112             ## MCE handles the reaping of its children.
113             $SIG{CHLD} = 'DEFAULT';
114             }
115              
116             my $_safe_clean = 0;
117              
118             sub _make_tmpdir {
119 34     34   95 my ($_count, $_tmp_base_dir) = (0);
120              
121 34 0 33     186 return $tmp_dir if (defined $tmp_dir && -d $tmp_dir && -w _);
      33        
122              
123 34 50 33     256 if ($ENV{TEMP} && -d $ENV{TEMP} && -w _) {
      33        
124 0 0       0 if ($^O =~ /mswin|mingw|msys|cygwin/i) {
125 0         0 $_tmp_base_dir = $ENV{TEMP} . '/Perl-MCE';
126 0 0       0 mkdir $_tmp_base_dir unless -d $_tmp_base_dir;
127             } else {
128 0         0 $_tmp_base_dir = $ENV{TEMP};
129             }
130             }
131             else {
132 34 100 66     210 $_tmp_base_dir = ($_use_dev_shm && -d '/dev/shm' && -w _)
133             ? '/dev/shm' : '/tmp';
134             }
135              
136             _croak("Error: MCE::Signal: ($_tmp_base_dir) is not writeable")
137 34 50 33     738 if (! exists $ENV{'MOBASTARTUPDIR'} && ! -w $_tmp_base_dir);
138              
139             ## Remove tainted'ness from $tmp_dir.
140 34         492 ($tmp_dir) = "$_tmp_base_dir/$prog_name.$$.$_count" =~ /(.*)/;
141              
142 34         113 while ( !(mkdir $tmp_dir, 0770) ) {
143 0         0 ($tmp_dir) = ("$_tmp_base_dir/$prog_name.$$.".(++$_count)) =~ /(.*)/;
144             }
145              
146 34         139 $_safe_clean = 1;
147              
148 34         125 return $tmp_dir;
149             }
150              
151             sub _remove_tmpdir {
152 9 50 33 9   28 return if (!defined $tmp_dir || $tmp_dir eq '' || ! -d $tmp_dir);
      33        
153              
154 9 50       60 if ($_keep_tmp_dir == 1) {
    50          
155 0         0 print {*STDERR} "$prog_name: saved tmp_dir = $tmp_dir\n";
  0         0  
156             }
157             elsif ($_safe_clean) {
158 9 50 33     124 if ($ENV{'TEMP'} && $^O =~ /mswin|mingw|msys|cygwin/i) {
159             ## remove tainted'ness
160 0         0 my ($_dir) = $ENV{'TEMP'} =~ /(.*)/;
161 0 0       0 chdir $_dir if -d $_dir;
162             }
163 9         35 rmdir $tmp_dir;
164 9 50       58 if (-d $tmp_dir) {
165 0         0 local $@; local $SIG{__DIE__};
  0         0  
166 0         0 eval 'require File::Path; File::Path::rmtree($tmp_dir)';
167             }
168             }
169              
170 9         62 $tmp_dir = undef;
171             }
172              
173             ###############################################################################
174             ## ----------------------------------------------------------------------------
175             ## Stops execution, removes temp directory and exits cleanly.
176             ##
177             ## Provides safe reentrant logic for parent and child processes.
178             ## The $main_proc_id variable is defined above.
179             ##
180             ###############################################################################
181              
182             BEGIN {
183 97     97   330 $MCE::Signal::IPC = 0; # 1 = defer signal_handling until completed IPC
184 97         139831 $MCE::Signal::SIG = ''; # signal received during IPC in MCE::Shared 1.863
185             }
186              
187             sub defer {
188 0 0   0 1 0 $MCE::Signal::SIG = $_[0] if $_[0];
189 0         0 return;
190             }
191              
192             my %_sig_name_lkup = map { $_ => 1 } qw(
193             __DIE__ ABRT HUP INT PIPE QUIT TERM __WARN__
194             );
195              
196             my $_count = 0;
197              
198             my $_handler_count = $INC{'threads/shared.pm'}
199             ? threads::shared::share($_count)
200             : \$_count;
201              
202             sub stop_and_exit {
203 22 50 33 22 1 401 shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal');
204 22 50       97 return MCE::Signal::defer($_[0]) if $MCE::Signal::IPC;
205              
206 22   50     190 my ($_exit_status, $_is_sig, $_sig_name) = ($?, 0, $_[0] || 0);
207 22     0   288 $SIG{__DIE__} = $SIG{__WARN__} = sub {};
208              
209 22 50       151 if (exists $_sig_name_lkup{$_sig_name}) {
210 0         0 $_exit_status = $MCE::Signal::KILLED = $_is_sig = 1;
211 0 0       0 $_exit_status = 255, $_sig_name = 'TERM' if ($_sig_name eq '__DIE__');
212 0 0       0 $_exit_status = 0 if ($_sig_name eq 'PIPE');
213 0     0   0 $SIG{INT} = $SIG{$_sig_name} = sub {};
214             }
215             else {
216 22 50       249 $_exit_status = $_sig_name if ($_sig_name =~ /^\d+$/);
217 22         69 $MCE::Signal::STOPPED = 1;
218             }
219              
220             ## Main process.
221 22 50       100 if ($$ == $main_proc_id) {
    0          
222              
223 22 50       53 if (++${ $_handler_count } == 1) {
  22         105  
224             ## Kill process group if signaled.
225 22 50       132 if ($_is_sig == 1) {
226 0 0       0 ($_sig_name eq 'PIPE')
    0          
    0          
227             ? CORE::kill('PIPE', $_is_MSWin32 ? -$$ : -getpgrp)
228             : CORE::kill('INT' , $_is_MSWin32 ? -$$ : -getpgrp);
229              
230 0 0       0 if ($_sig_name eq 'PIPE') {
231 0         0 for my $_i (1..2) { Time::HiRes::sleep(0.015); }
  0         0  
232             } else {
233 0         0 for my $_i (1..3) { Time::HiRes::sleep(0.060); }
  0         0  
234             }
235             }
236              
237             ## Remove temp directory.
238 22 100       276 _remove_tmpdir() if defined($tmp_dir);
239              
240             ## Signal process group to die.
241 22 50       94 if ($_is_sig == 1) {
242 0 0 0     0 if ($_sig_name eq 'INT' && -t STDIN) { ## no critic
243 0         0 print {*STDERR} "\n";
  0         0  
244             }
245 0 0 0     0 if ($INC{'threads.pm'} && ($] lt '5.012000' || threads->tid())) {
      0        
246 0 0 0     0 ($_no_kill9 == 1 || $_sig_name eq 'PIPE')
    0          
247             ? CORE::kill('INT', $_is_MSWin32 ? -$$ : -getpgrp)
248             : CORE::kill('KILL', -$$);
249             }
250             else {
251 0 0       0 CORE::kill('INT', $_is_MSWin32 ? -$$ : -getpgrp);
252             }
253             }
254             }
255             }
256              
257             ## Child processes.
258             elsif ($_is_sig) {
259              
260             ## Windows support, from nested workers.
261 0 0       0 if ($_is_MSWin32) {
262 0 0       0 _remove_tmpdir() if defined($tmp_dir);
263 0         0 CORE::kill('KILL', $main_proc_id, -$$);
264             }
265              
266             ## Real child processes.
267             else {
268 0         0 CORE::kill($_sig_name, $main_proc_id, -$$);
269 0         0 CORE::kill('KILL', -$$, $$);
270             }
271             }
272              
273             ## Exit with status.
274 22         901 CORE::exit($_exit_status);
275             }
276              
277             ###############################################################################
278             ## ----------------------------------------------------------------------------
279             ## Run command via the system(...) function.
280             ##
281             ## The system function in Perl ignores SIGINT and SIGQUIT. These 2 signals
282             ## are sent to the command being executed via system() but not back to
283             ## the underlying Perl script. The code below will ensure the Perl script
284             ## receives the same signal in order to raise an exception immediately
285             ## after the system call.
286             ##
287             ## Returns the actual exit status.
288             ##
289             ###############################################################################
290              
291             sub sys_cmd {
292 0 0 0 0 1 0 shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal');
293              
294 0 0       0 _croak('MCE::Signal::sys_cmd: no arguments were specified') if (@_ == 0);
295              
296 0         0 my $_status = system(@_);
297 0         0 my $_sig_no = $_status & 127;
298 0         0 my $_exit_status = $_status >> 8;
299              
300             ## Kill the process group if command caught SIGINT or SIGQUIT.
301              
302 0 0       0 CORE::kill('INT', $main_proc_id, $_is_MSWin32 ? -$$ : -getpgrp)
    0          
303             if $_sig_no == 2;
304              
305 0 0       0 CORE::kill('QUIT', $main_proc_id, $_is_MSWin32 ? -$$ : -getpgrp)
    0          
306             if $_sig_no == 3;
307              
308 0         0 return $_exit_status;
309             }
310              
311             ###############################################################################
312             ## ----------------------------------------------------------------------------
313             ## Signal handlers for __DIE__ & __WARN__ utilized by MCE.
314             ##
315             ###############################################################################
316              
317             sub _die_handler {
318 0 0 0 0   0 shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal');
319              
320 0 0 0     0 if (!defined $^S || $^S) {
321 0 0 0     0 if ( ($INC{'threads.pm'} && threads->tid() != 0) ||
      0        
322             $ENV{'PERL_IPERL_RUNNING'}
323             ) {
324             # thread env or running inside IPerl, check stack trace
325 0         0 my $_t = Carp::longmess(); $_t =~ s/\teval [^\n]+\n$//;
  0         0  
326 0 0 0     0 if ( $_t =~ /^(?:[^\n]+\n){1,7}\teval / ||
327             $_t =~ /\n\teval [^\n]+\n\t(?:eval|Try)/ )
328             {
329 0         0 CORE::die(@_);
330             }
331             }
332             else {
333             # normal env, trust $^S
334 0         0 CORE::die(@_);
335             }
336             }
337              
338 0         0 local $\ = undef;
339              
340             ## Set $MCE::Signal::display_die_with_localtime = 1;
341             ## when wanting the output to contain the localtime.
342              
343 0 0       0 if (defined $_[0]) {
344 0         0 my $mesg = $_[0]; $mesg =~ s/, <__ANONIO__> line \d+//;
  0         0  
345 0 0       0 if ($MCE::Signal::display_die_with_localtime) {
346 0         0 my $_time_stamp = localtime;
347 0         0 print {*STDERR} "## $_time_stamp: $prog_name: ERROR:\n", $mesg;
  0         0  
348             }
349             else {
350 0         0 print {*STDERR} $mesg;
  0         0  
351             }
352             }
353              
354 0         0 MCE::Signal::stop_and_exit('__DIE__');
355             }
356              
357             sub _warn_handler {
358 0 0 0 0   0 shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal');
359              
360             ## Ignore thread warnings during exiting.
361              
362             return if (
363 0 0 0     0 $_[0] =~ /^Finished with active (?:child|hobo) processes/ ||
      0        
      0        
      0        
364             $_[0] =~ /^A thread exited while \d+ threads were running/ ||
365             $_[0] =~ /^Attempt to free unreferenced scalar/ ||
366             $_[0] =~ /^Perl exited with active threads/ ||
367             $_[0] =~ /^Thread \d+ terminated abnormally/
368             );
369              
370 0         0 local $\ = undef;
371              
372             ## Set $MCE::Signal::display_warn_with_localtime = 1;
373             ## when wanting the output to contain the localtime.
374              
375 0 0       0 if (defined $_[0]) {
376 0         0 my $mesg = $_[0]; $mesg =~ s/, <__ANONIO__> line \d+//;
  0         0  
377 0 0       0 if ($MCE::Signal::display_warn_with_localtime) {
378 0         0 my $_time_stamp = localtime;
379 0         0 print {*STDERR} "## $_time_stamp: $prog_name: WARNING:\n", $mesg;
  0         0  
380             }
381             else {
382 0         0 print {*STDERR} $mesg;
  0         0  
383             }
384             }
385              
386 0         0 return;
387             }
388              
389             1;
390              
391             ###############################################################################
392             ## ----------------------------------------------------------------------------
393             ## TIE scalar package for making $MCE::Signal::tmp_dir on demand.
394             ##
395             ###############################################################################
396              
397             package MCE::Signal::_tmpdir;
398              
399             sub TIESCALAR {
400 97     97   193 my $_class = shift;
401 97 50       141 bless \do{ my $o = defined $_[0] ? shift : undef }, $_class;
  97         537  
402             }
403              
404             sub STORE {
405 43     43   411 ${ $_[0] } = $_[1];
  43         247  
406              
407 43 100       353 $_safe_clean = 0 if ( length $_[1] < 9 );
408 43 50 33     307 $_safe_clean = 0 if ( $ENV{'TEMP'} && $ENV{'TEMP'} eq $_[1] );
409 43 50       204 $_safe_clean = 0 if ( $_[1] =~ m{[\\/](?:etc|bin|lib|sbin)} );
410 43 50       147 $_safe_clean = 0 if ( $_[1] =~ m{[\\/](?:temp|tmp)[\\/]?$}i );
411              
412 43         125 $_[1];
413             }
414              
415             sub FETCH {
416 805 100   805   1713 if (!defined ${ $_[0] }) {
  805         2684  
417 595         1137 my $_caller = caller();
418 595 50 66     2142 if ($_caller ne 'MCE' && $_caller ne 'MCE::Signal') {
419 0 0 0     0 if ($INC{'MCE.pm'} && MCE->wid() > 0) {
420 0         0 ${ $_[0] } = MCE->tmp_dir();
  0         0  
421             } else {
422 0         0 ${ $_[0] } = MCE::Signal::_make_tmpdir();
  0         0  
423             }
424             }
425             }
426 805         1048 ${ $_[0] };
  805         8630  
427             }
428              
429             1;
430              
431             __END__
432              
433             ###############################################################################
434             ## ----------------------------------------------------------------------------
435             ## Module usage.
436             ##
437             ###############################################################################
438              
439             =head1 NAME
440              
441             MCE::Signal - Temporary directory creation/cleanup and signal handling
442              
443             =head1 VERSION
444              
445             This document describes MCE::Signal version 1.887
446              
447             =head1 SYNOPSIS
448              
449             ## Creates tmp_dir under $ENV{TEMP} if defined, otherwise /tmp.
450              
451             use MCE::Signal;
452              
453             ## Attempts to create tmp_dir under /dev/shm if writable.
454              
455             use MCE::Signal qw( -use_dev_shm );
456              
457             ## Keeps tmp_dir after the script terminates.
458              
459             use MCE::Signal qw( -keep_tmp_dir );
460             use MCE::Signal qw( -use_dev_shm -keep_tmp_dir );
461              
462             ## MCE loads MCE::Signal by default when not present.
463             ## Therefore, load MCE::Signal first for options to take effect.
464              
465             use MCE::Signal qw( -keep_tmp_dir -use_dev_shm );
466             use MCE;
467              
468             =head1 DESCRIPTION
469              
470             This package configures $SIG{ ABRT, HUP, INT, PIPE, QUIT, and TERM } to
471             point to stop_and_exit and creates a temporary directory. The main process
472             and workers receiving said signals call stop_and_exit, which signals all
473             workers to terminate, removes the temporary directory unless -keep_tmp_dir
474             is specified, and terminates itself.
475              
476             The location of the temp directory resides under $ENV{TEMP} if defined,
477             otherwise /dev/shm if writeable and -use_dev_shm is specified, or /tmp.
478             On Windows, the temp directory is made under $ENV{TEMP}/Perl-MCE/.
479              
480             As of MCE 1.405, MCE::Signal no longer calls setpgrp by default. Pass the
481             -setpgrp option to MCE::Signal to call setpgrp.
482              
483             ## Running MCE through Daemon::Control requires setpgrp to be called
484             ## for MCE releases 1.511 and below.
485              
486             use MCE::Signal qw(-setpgrp); ## Not necessary for MCE 1.512 and above
487             use MCE;
488              
489             The following are available options and their meanings.
490              
491             -keep_tmp_dir - The temporary directory is not removed during exiting
492             A message is displayed with the location afterwards
493              
494             -use_dev_shm - Create the temporary directory under /dev/shm
495             -no_kill9 - Do not kill -9 after receiving a signal to terminate
496              
497             -setpgrp - Calls setpgrp to set the process group for the process
498             This option ensures all workers terminate when reading
499             STDIN for MCE releases 1.511 and below.
500              
501             cat big_input_file | ./mce_script.pl | head -10
502              
503             This works fine without the -setpgrp option:
504              
505             ./mce_script.pl < big_input_file | head -10
506              
507             Nothing is exported by default. Exportable are 1 variable and 2 subroutines.
508              
509             use MCE::Signal qw( $tmp_dir stop_and_exit sys_cmd );
510             use MCE::Signal qw( :all );
511              
512             $tmp_dir - Path to the temporary directory.
513             stop_and_exit - Described below
514             sys_cmd - Described below
515              
516             =head2 stop_and_exit ( [ $exit_status | $signal ] )
517              
518             Stops execution, removes temp directory, and exits the entire application.
519             Pass 'INT' to terminate a spawned or running MCE session.
520              
521             MCE::Signal::stop_and_exit(1);
522             MCE::Signal::stop_and_exit('INT');
523              
524             =head2 sys_cmd ( $command )
525              
526             The system function in Perl ignores SIGINT and SIGQUIT. These 2 signals are
527             sent to the command being executed via system() but not back to the underlying
528             Perl script. For this reason, sys_cmd was added to MCE::Signal.
529              
530             ## Execute command and return the actual exit status. The perl script
531             ## is also signaled if command caught SIGINT or SIGQUIT.
532              
533             use MCE::Signal qw(sys_cmd); ## Include before MCE
534             use MCE;
535              
536             my $exit_status = sys_cmd($command);
537              
538             =head1 DEFER SIGNAL
539              
540             =head2 defer ( $signal )
541              
542             Returns immediately inside a signal handler if signaled during IPC.
543             The signal is deferred momentarily and re-signaled automatically upon
544             completing IPC. Currently, all IPC related methods in C<MCE::Shared> and
545             one method C<send2> in C<MCE::Channel> set the flag C<$MCE::Signal::IPC>
546             before initiating IPC.
547              
548             Current API available since 1.863.
549              
550             sub sig_handler {
551             return MCE::Signal::defer($_[0]) if $MCE::Signal::IPC;
552             ...
553             }
554              
555             In a nutshell, C<defer> helps safeguard IPC from stalling between workers
556             and the shared manager-process. The following is a demonstration for Unix
557             platforms. Deferring the signal inside the C<WINCH> handler prevents the
558             app from eventually failing while resizing the window.
559              
560             use strict;
561             use warnings;
562              
563             use MCE::Hobo;
564             use MCE::Shared;
565             use Time::HiRes 'sleep';
566              
567             my $count = MCE::Shared->scalar(0);
568             my $winch = MCE::Shared->scalar(0);
569             my $done = MCE::Shared->scalar(0);
570              
571             $SIG{WINCH} = sub {
572             # defer signal if signaled during IPC
573             return MCE::Signal::defer($_[0]) if $MCE::Signal::IPC;
574              
575             # mask signal handler
576             local $SIG{$_[0]} = 'IGNORE';
577              
578             printf "inside winch handler %d\n", $winch->incr;
579             };
580              
581             $SIG{INT} = sub {
582             # defer signal if signaled during IPC
583             return MCE::Signal::defer($_[0]) if $MCE::Signal::IPC;
584              
585             # set flag for workers to leave loop
586             $done->set(1);
587             };
588              
589             sub task {
590             while ( ! $done->get ) {
591             $count->incr;
592             sleep 0.03;
593             };
594             }
595              
596             print "Resize the terminal window continuously.\n";
597             print "Press Ctrl-C to stop.\n";
598              
599             MCE::Hobo->create('task') for 1..8;
600             sleep 0.015 until $done->get;
601             MCE::Hobo->wait_all;
602              
603             printf "\ncount incremented %d times\n\n", $count->get;
604              
605             =head1 INDEX
606              
607             L<MCE|MCE>, L<MCE::Core>
608              
609             =head1 AUTHOR
610              
611             Mario E. Roy, S<E<lt>marioeroy AT gmail DOT comE<gt>>
612              
613             =cut
614