vishi has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,
I am testing a small program to understand how interrupt handling works in a program that uses threads and the "expect.pm" module. With this code, I am calling a shell script (which basically just sleeps for X seconds - to simulate some other actual program I intend to use my Perl code for).

Lets assume my code does a bit of critical stuff and a bunch of other non critical stuff. When I get a Ctrl C while the critical stuff is running, I want the program to wait until it completes processing and then quit. Also, when it gets a Ctrl C while doing the non critical stuff, it can go ahead and abort right away (of course after performing cleanup and other housekeeping stuff)

Here's my code that does this work:

#!/usr/bin/perl use strict; #use warnings; use Term::ReadKey; use Text::Wrap; use Term::Prompt; use Expect; use threads; use threads::shared; use Term::ANSIColor; use File::Path; use HTML::Entities; use MIME::QuotedPrint; use MIME::Base64; use MIME::Lite; use Date::Calc qw(Delta_DHMS); my $pass = "<password>"; my $User = "<username>"; $SIG{'INT' } = 'interrupt'; $SIG{'QUIT'} = 'interrupt'; $SIG{'HUP' } = 'interrupt'; $SIG{'TRAP'} = 'interrupt'; $SIG{'ABRT'} = 'interrupt'; $SIG{'STOP'} = 'interrupt'; my $sshAuth = Expect->spawn("ssh $User\@hostname"); $sshAuth->log_stdout(0); my $thr1 = threads->create('thread1',$sshAuth); $thr1->join(); sub thread1 { my $sshAuth = shift; our $sleepFlag = ''; $sshAuth->expect(1000, [qr/[Yy]es/ => sub {$sshAuth->send(" +yes\n");}], [qr/[Pp]assword\:\s/ => sub {$sshAuth->send("$pass +\n");}] ); $sshAuth->log_stdout(0); $sshAuth->expect(1000, [qr/\$/ => sub {$sshAuth->send("\n");}]) +; $sshAuth->log_stdout(0); $sshAuth->expect(1000, [qr/\$/ => sub {$sshAuth->send("cd Perl\ +/Test\n");}]); $sshAuth->log_stdout(0); ###### Here are the critical tasks that should not abort on Ctrl+C + ######### our $sleepFlag = "true"; $sshAuth->expect(1000, [qr/\$/ => sub {$sshAuth->send("\.\/slee +per\.sh\n");}]); $sshAuth->log_stdout(0); print "Entering Critical sleeping ... \n"; $sshAuth->expect(1000, [qr/\$[\s]*/ => sub {$sshAuth->send( +"\n");}]); $sshAuth->log_stdout(0); print "Woke up \n"; our $sleepFlag = "false"; ###### I really dont mind if the stuff here terminates on Ctrl+C # +######## $sshAuth->expect(1000, [qr/\$/ => sub {$sshAuth->send("\.\/slee +per\.sh\n");}]); $sshAuth->log_stdout(0); print "Entering Non Critical sleeping ... \n"; $sshAuth->expect(1000, [qr/\$[\s]*/ => sub {$sshAuth->send("\n" +);}]); $sshAuth->log_stdout(0); print "Woke up \n"; $sshAuth->close(); } sub interrupt { our $sleepFlag:shared; print "$sleepFlag\n"; print "Waiting for critical tasks to finish before terminating pro +gram"; while ($sleepFlag eq "true") { print "."; sleep(3); } print "\n\nNo critical tasks are running... it\'s safe to terminat +e now...\n"; print "Terminating Program!!!\n"; }

By the way, the sleeper.sh script, that is used to simulate a task that my script runs contains this:

sleep 60 ;

When I press Ctrl C while this code is running, nothing happens. As per my understanding, while in the critical execution phase, a Ctrl C should give me the message

Waiting for critical tasks to finish before terminating program

and while I press Ctrl C when the code is running in the non critical execution phase, it should print

No critical tasks are running... it's safe to terminate now... Terminating Program!!!

But as I see, nothing happens until the thread completes execution and is joined in the main thread. What am I doing wrong here?

Replies are listed 'Best First'.
Re: Interrupt Handling - What am I doing wrong?
by ikegami (Patriarch) on Apr 20, 2011 at 07:44 UTC

    Your debug code is suffering from buffering. Add $| = 1; and report back.

    You've got a problem is the Expect thread is the one that services the signal. Is that it? Don't know. Gotta run.

      Nope..buffering is not the issue .... I had the $| = 1 set; just forgot to paste it in the code above.

      Is the behaviour of the code due to "sleep", i.e., am telling my program to sleep and not do anything else...so, its not doing anything else (like printing the stuff it's supposed to)...? If so, is there something I can use to simulate a long running process (that runs for about 2-3 mins)?

      I'm out of ideas for this one.. been trying all morning without luck!

        Do you really not get "Waiting for critical tasks to finish before terminating program"? That would mean your signal handler isn't called at all.

        Either way, I already gave a reason the current code won't always work. Executing the signal handler will prevent (at least) that thread from doing anything else. If the signal handler is called from a thread1 thread, that thread1 thread won't be able to progress.

Re: Interrupt Handling - What am I doing wrong?
by ikegami (Patriarch) on Apr 20, 2011 at 18:10 UTC
    #!/usr/bin/perl use strict; use warnings; my $in_critical = 0; my $interrupt = 0; $SIG{'INT' } = \&interrupt; $SIG{'QUIT'} = \&interrupt; $SIG{'HUP' } = \&interrupt; $SIG{'TRAP'} = \&interrupt; $SIG{'ABRT'} = \&interrupt; $SIG{'STOP'} = \&interrupt; sub enter_critical { ++$in_critical; } sub leave_critical { --$in_critical; if ($interrupt) { $interrupt = 0; die("INTERRUPTED\n"); } } sub interrupt { if ($in_critical) { ++$interrupt; } else { die("INTERRUPTED\n"); } } ... don't even need threads ...