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

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

Hi Monks,

I have a Tkx gui which runs a batch file using button. The batch file is executed in a different thread, cause I want the GUI to still be usable. I want to implement a cancel button to cancel the execution of the batch file.

I tried sending a Kill signal but it will only terminate the thread and not the batch file. Below is the code. Thanks! Oh and I am not allowed to edit the batch file. =/

my $t1; sub runbutton{ $bar->g_grid(); $bar->start(); $t1 = threads->create(sub { local $SIG{'KILL'} = sub { threads->exit }; system("timer.bat"); }); $t1->set_thread_exit_only(1); my $start = time; my $end = time; while ($t1->is_running()) { $end = time(); $mytext = sprintf("%.2f\n", $end - $start); Tkx::update(); } $bar->stop(); $bar->g_grid_forget(); $b4->g_grid_forget(); } sub cancelbutton { $t1->kill('KILL')->detach(); }

Replies are listed 'Best First'.
Re: Cancel a batch file ran by a thread from main
by zentara (Archbishop) on May 09, 2014 at 13:26 UTC
    I don't have Tkx going, but in Tk, you could do it like this. In the thread, when you launch your program, don't use system, but use a form of IPC which will return a pid for the batch file running. Then stuff the pid in a threads shared variable, then in your main Tkx script, the cancel button would invoke "killfam 9, $pid". Use Proc::Killfam to make sure you kill not only the batch file, but any shell which gets invoked to run it.

    A simple example in Tk, to show the principle. Tkx should work similarly.

    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; use Tk; my %shash; #share(%shash); #will work only for first level keys my %hash; share ($shash{'go'}); share ($shash{'fileno'}); share ($shash{'pid'}); share ($shash{'die'}); $shash{'go'} = 0; $shash{'fileno'} = -1; $shash{'pid'} = -1; $shash{'die'} = 0; $hash{'thread'} = threads->new(\&work); my $mw = MainWindow->new(-background => 'gray50'); my $text = $mw->Scrolled('Text')->pack(); my $startb = $mw->Button( -text => 'Start', -command=>sub{ $shash{'go'} = 1; $mw->after(100); #give pipe chance to startup my $fileno = $shash{'fileno'}; print "fileno_m $fileno\n"; open (my $fh, "<&=$fileno") or warn "$!\n"; # filevent works but may not work on win32, # but you can use a timer instead as shown below $mw->fileevent(\*$fh, 'readable', ); while(<$fh>){ $text->insert('end',$_); $text->see('end'); $mw->update; } # on Win32 (untested by me) you will need # a timer instead of fileevent # my $repeater; # $repeater = $mw->repeat(10, # sub { # my $bytes = sysread( "<&=$fileno", my $buf, 8192); # $text->insert('end',$buf); # $text->see('end'); # if( $shash{'go'} == 0 ){ $repeater->cancel } # } # ); } )->pack(); my $stopb = $mw->Button( -text => 'Stop/Exit', -command=>sub{ $shash{'die'} = 1; kill 9,$shash{'pid'}; $hash{'thread'}->join; exit; }, )->pack(); MainLoop; ################################################################## sub work{ $|++; while(1){ if($shash{'die'} == 1){ return }; if ( $shash{'go'} == 1 ){ #run your command here, and try to find a way to capture it's +output # on win32 you may need to use IPC::Run in the thread #use a win32 command that gives continous output # on linux I use top my $pid = open(FH, "top -b |" ) or warn "$!\n"; my $fileno = fileno(FH); print "fileno_t->$fileno\n"; $shash{'fileno'} = $fileno; $shash{'pid'} = $pid; $shash{'go'} = 0; #turn off self before returning }else { select(undef,undef,undef,.1) } #short sleep } } #####################################################################

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Cancel a thread running a batch file from main
by Anonymous Monk on May 09, 2014 at 07:02 UTC
      Hi, firstly thanks for your support regarding my thread issues. :) I was not able to try out the Proc::Background method cause our active state version does not have this package by default. (well i can download it i know, but as other people might try to run this code, i figured i'd stick to the common packages). Anyway, I looked at your thread though and tried the queuing and seems like it's the (thread-safe) way to go and is a much better design, but I still end up with the same problem. Though now i think I found something is wrong with my question, it should have read "Cancel the BATCH file executed by a system call from a thread in main." pardon my mistake. any thoughts?
      my @jobs ; ... push @jobs, Proc::Background->new( $command, $arg1, $arg2 ); ... $jobs[0]->die if $jobs[0]->alive;