#!/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";
}