#!/usr/bin/perl ########################################################### # USES ########################################################### use strict; use warnings; use Tk; use My::Gui; use My::ThreadManager; ########################################################### # MAIN FUNCTION ########################################################### &createMyThread(); &createMyGui(); &MainLoop(); #### package My::Globals; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use constant { FALSE => 0, TRUE => 1, CANCEL => 2 }; ########################################################### # EXPORT ########################################################### our @EXPORT = qw(TRUE FALSE CANCEL); ########################################################### # VARIABLES ########################################################### # NONE ########################################################### # PUBLIC FUNCTIONS ########################################################### # NONE ########################################################### # PRIVATE FUNCTIONS ########################################################### # NONE 1; #### package My::Gui; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use Tk; use My::ThreadManager; ########################################################### # EXPORT ########################################################### our @EXPORT = qw(createMyGui); ########################################################### # VARIABLES ########################################################### my %gui; my %thread_data = ( 'number' => 7 ); ########################################################### # PUBLIC FUNCTIONS ########################################################### sub createMyGui { $gui{'mw'} = new MainWindow; $gui{'mw'}->title("Thread Example"); $gui{'mw'}->protocol('WM_DELETE_WINDOW' => sub { evWorkCancel() unless isWorkFinished(); killMyThread(); }); fillMainWindow($gui{'mw'}); } ########################################################### # PRIVATE FUNCTIONS ########################################################### sub fillMainWindow { my $mw = $_[0]; $mw->{'start_b'} = $mw->Button('-relief' => 'raised', '-text' => 'START', '-command' => sub { evWorkStart(\%thread_data); }); $mw->{'cancel_b'} = $mw->Button('-relief' => 'raised', '-text' => 'CANCEL', '-command' => sub { evWorkCancel(); }); $mw->{'start_b'}->pack(); $mw->{'cancel_b'}->pack(); } 1; #### package My::ThreadManager; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use Data::Dumper; use threads; use threads::shared; use Thread::Queue; use My::Globals; use My::ThreadWorker; use feature qw(say); use constant { EV_NONE => 0, EV_KILL => 1, EV_WORK_START => 2, EV_WORK_CANCEL => 3, EV_WORK_FINISH => 4 }; use constant { STATE_IDLE => 0, STATE_WORK => 1 }; ########################################################### # EXPORT ########################################################### our @EXPORT = qw(createMyThread killMyThread evWorkStart evWorkCancel isWorkFinished shallWorkBeCancelled ); ########################################################### # VARIABLES ########################################################### my $thread; my $thread_event:shared = EV_NONE; my $thread_state:shared = STATE_IDLE; my $q = Thread::Queue->new(); ########################################################### # PUBLIC FUNCTIONS ########################################################### sub createMyThread { $thread = threads->create( \&execMyThread ); } sub execMyThread { STATE_IDLE: $thread_state = STATE_IDLE; say "STATE_IDLE"; while(1) { if( $thread_event == EV_WORK_START ) { say "EV_WORK_START"; $thread_event = EV_NONE; goto STATE_WORK; } elsif( $thread_event == EV_KILL ) { say "EV_KILL"; $thread_event = EV_NONE; return; } else { # wait select(undef,undef,undef,0.1); } } STATE_WORK: $thread_state = STATE_WORK; say "STATE_WORK"; while(1) { if( ($thread_event == EV_WORK_FINISH) || ($thread_event == EV_WORK_CANCEL) ) { say "EV_WORK_FINISH" if( $thread_event == EV_WORK_FINISH ); say "EV_WORK_CANCEL" if( $thread_event == EV_WORK_CANCEL ); $thread_event = EV_NONE; goto STATE_IDLE; } elsif( $thread_event == EV_KILL ) { say "EV_KILL"; $thread_event = EV_NONE; return; } else { my %thread_data; select(undef,undef,undef,0.1); while( my $thread_data_str1 = $q->dequeue ) { %thread_data = %{ eval $thread_data_str1 }; }; if( &work(\%thread_data) == CANCEL ) { $thread_event = EV_WORK_CANCEL; } else { $thread_event = EV_WORK_FINISH; } } } } sub killMyThread { $thread_event = EV_KILL; $thread->join; exit 0; } sub evWorkStart { my $ref_thread_data = $_[0]; my $thread_data_str1 = ""; $Data::Dumper::Varname = "thread_data_str"; $thread_data_str1 = Dumper($ref_thread_data); $q->enqueue( $thread_data_str1 ); $q->enqueue( undef ); $thread_event = EV_WORK_START; while( $thread_state != STATE_WORK ) { select(undef, undef, undef, 0.1); } } sub evWorkCancel { $thread_event = EV_WORK_CANCEL; while( $thread_state != STATE_IDLE ) { select(undef, undef, undef, 0.1); } } sub isWorkFinished { if( $thread_state == STATE_IDLE ) { return TRUE; } else { return FALSE; } } sub shallWorkBeCancelled { if( $thread_event == EV_WORK_CANCEL ) { return TRUE; } else { return FALSE; } } ########################################################### # PRIVATE FUNCTIONS ########################################################### # NONE 1; #### package My::ThreadWorker; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use My::Globals; use My::ThreadManager; use Data::Dumper; use feature qw(say); ########################################################### # EXPORT ########################################################### our @EXPORT = qw( work ); ########################################################### # VARIABLES ########################################################### # NONE ########################################################### # PUBLIC FUNCTIONS ########################################################### sub work { my ($ref_thread_data) = @_; while( $ref_thread_data->{'number'} <= 100 ) { print $ref_thread_data->{'number'} . " "; select(undef, undef, undef, 0.1); $ref_thread_data->{'number'} += 1; # NOTE: # Bad design because My::ThreadWorker needs # My::ThreadManager and My::ThreadManager # needs MyThreadWorker if( My::ThreadManager::shallWorkBeCancelled() == TRUE ) { print "\n"; return CANCEL; } } print "\n"; }