http://www.perlmonks.org?node_id=11160785

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

I am having trouble with interrupting Perl as it processes a loop with a ‘sleep’ in it.
I am using Perl on a Windows 11 pc.
The included code runs a loop. At the start of the loop is ${INT} which sets a sub to be used if CNTRL-C is used. This sub simply sets the variable $loop_test to zero so that the loop ends.
This test Perl works. However, when I use this is the ‘production’ application, where a sub is also called (this tests if a file has recently being store and then acts on this information) CNTRL-C does not work.
Has any Monk a suggestion as to how this can be made to work?
use strict; use warnings; my ($loop_test, $cou); $loop_test = 1; $cou = 0; while($loop_test > 0) { # something which the lets the user set $loop#_tes to 0 $SIG{INT} = \&tsktsk; sleep(5); $cou = $cou +1; print "end sleep - count $cou\n"; # sub to do something - for exmaple see if a files mod time has change +d } print "\nEND\n"; sub tsktsk{ $loop_test = 0; print "[tsktsk] called\n"; }

I am using Perl Tk and could have a button which when used has the effect of calling the sub to set a variable to end the loop
My attempts to do this have failed because the button fails to respond.
I seem to remember (it is a few years since I have written any Perl Tk code) that there is some property that can be assigned to a button so that it would work in these circumstances.
However, my searches for this have failed.
Can any Monk point me it the right direction?

Replies are listed 'Best First'.
Re: Interrupting a loop
by choroba (Cardinal) on Jul 28, 2024 at 08:34 UTC
    Under Tk, Ctrl+C no longer sends the INT signal. You can still bind <Control-c> to something, though.

    Also, Tk already incorporates a loop: the MainLoop. Looping over short lists is probably OK, but introducing loops that might take a long time to iterate or have a high number of iterations is wrong, you should use Tk->repeat or Tk->after instead, see Tk::after.

    #!/usr/bin/perl use warnings; use strict; use Tk; sub periodically { warn "Testing the file existence...\n"; } my $mw = MainWindow->new(-title => 'Test'); my $repeat = $mw->repeat(1000, \&periodically); $mw->bind('<Control-c>', sub { $mw->afterCancel($repeat) }); MainLoop();

    Update: Added the example.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      To emphasise a point in choroba's excellent thought: when you want to juggle multiple events (a signal/button-click AND elapsed time), you need to stay in the idiom of the relevant event loop. In Tk, that's why you'd want to do the time-delay with Tk::after, not with sleep, so the event loop can quietly also watch for the button-click etc and react; sleep just suspends the whole process until the OS sends it a SIGALRM.
      Thank you. I had not come across Tk->repeat etc before.
      So I now have my interruptible Perl Tk app working now.
        The reason I wanted CNTR-C to work was that the Buttons on my Tk GUI would not respond when the loop was processing.
        I now have found that using the Tk repeat for the loop means that I can use the Buttons.
        So the solution was even better than I first thought.

        You should be using <br/>, not </br>. TIA. (I fixed your root post already.)