package Step::Base; use v5.10.1; use threads; use threads::shared; use namespace::autoclean; #use feature qw( switch ); use Moose; has 'name' => ( is => 'ro', isa => 'Str', required => 1, ); has 'code_to_run' => ( is => 'ro', isa => 'Str', required => 1, ); has 'args' => ( is => 'ro', isa => 'ArrayRef', ); has 'return_code' => ( is => 'rw', isa => 'ArrayRef', ); has 'state' => ( is => 'rw', isa => 'Str', default => 'Created', ); has 'success' => ( is => 'rw', isa => 'Int', ); has 'error' => ( is => 'rw', isa => 'Str', ); has 'time_started' => ( is => 'rw', isa => 'Int', ); has 'time_finished' => ( is => 'rw', isa => 'Int', ); has 'success' => ( is => 'rw', isa => 'Int', ); has 'predecessors' => ( is => 'ro', isa => 'ArrayRef', ); has 'thr' => ( is => 'rw', isa => 'Object', ); has 'steps' => ( is => 'ro', ); sub run { my ( $self, ) = @_; $self->time_started(time); my $thr = threads->create( $self->code_to_run, $self->args ); if ( $thr->error ) { $self->state('Abended'); $self->error(@$); $self->time_finished(time); $self->success(0); return( 0 ); } else { $self->thr($thr); $self->success(1); $self->state('Running'); return( 1 ); } } ## ---------- end sub run sub ready { my ( $self, ) = @_; return(0) unless $self->state eq 'Created'; $self->_predecessors_complete( $self->predecessors ) ? return(1) : return(0); } ## ---------- end sub ready sub running { my ( $self, ) = @_; $self->state eq "Running" ? return(1) : return(0) } ## ---------- end sub running sub done { my ( $self, ) = @_; # check to see if thread is ready to join and join if it is given( $self->state ) { when( 'Running' ) { my @joinable = threads->list(threads::joinable); foreach my $thr (@joinable) { next unless ( $thr->tid == $self->thr->tid ); my $rc = $thr->join; if ( $rc->{success} ) { $self->state('Completed'); } else { $self->state('Abended'); my $err_msg; if ( my $thread_error = $thr->error ) { $err_msg = "$thread_error\n" } $err_msg .= $rc->{err_msg}; $self->error($err_msg); } $self->time_finished(time); return (1); } return (0); } when( 'Completed' ) { return(1); } when( 'Abended' ) { return(1); } when( 'Cancelled' ) { return(1); } default { return (0); } } } ## ---------- end sub done sub _predecessors_complete { my ( $self, $predecessors ) = @_; foreach my $p ( @{ $predecessors } ) { #given( $main::steps->{ $p }->state ) { given( $self->steps->{ $p }->state ) { when ("Complete") { next; } when ("Created") { return(0); } when ("Running") { return(0); } when ("Cancelled") { $self->state("Cancelled"); return(0); } when ("Abended") { $self->state("Cancelled"); return(0); } } } return ( 1 ); } ## ---------- end sub _predecessors_status sub time_elapsed { my ( $self, ) = @_; given( $self->state ) { when( 'Running' ) { return ( time - $self->time_started ); } when( 'Completed' ) { return ( $self->time_finished - $self->time_started ); } default { return (undef); } } } ## ---------- end sub time_elapsed 1; __PACKAGE__->meta->make_immutable; #### use strict; use warnings; use v5.10.1; use Step::Base; my ( $steps, $step_names ) = load_steps(); while (1) { my $cnt_not_ready = 0; my $cnt_running = 0; my $cnt_done = 0; FOR: foreach my $name ( @{$step_names} ) { if ( $steps->{$name}->done ) { $cnt_done++; } elsif ( $steps->{$name}->state eq 'Created' ) { if ( $steps->{$name}->ready ) { $steps->{$name}->run ? $cnt_running++ : $cnt_done++; } else { $cnt_not_ready++; } } elsif ( $steps->{$name}->running ) { $cnt_running++; } else { die "Something ain't right!\n" . "$name\-\>state = " . $steps->{$name}->state . "\n" . "\tRunning: " . $steps->{$name}->running . "\n" . "\t Done: " . $steps->{$name}->done . "\n"; } printf( "%-15s %s\n", $steps->{$name}->state, $name ); } printf( "Not Ready: %d Running: %d Done: %d\n", $cnt_not_ready, $cnt_running, $cnt_done ); last if ( $cnt_done >= scalar( @{$step_names} ) ); sleep(5); } sub load_steps { my $steps; my $step_names; my $conf = define_steps_conf(); foreach my $step ( @{$conf} ) { my ( $name, $args ) = each %{$step}; $args->{name} = $name; $args->{steps} = $steps; my $s = Step::Base->new($args); $steps->{$name} = $s; push( @{$step_names}, $name ); 1; } return ( $steps, $step_names ); } ## ---------- end sub load_steps sub define_steps_conf { my $steps = [ { 'Action01' => { code_to_run => 'main::do_nothing', predecessors => [], } }, { 'Action01a', => { code_to_run => 'main::do_nothing', predecessors => [ 'Action01', ], } }, { 'Action02' => { code_to_run => 'main::do_nothing', predecessors => [], } }, { 'Action02a' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action02', ], } }, { 'Action03' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action01a', 'Action02a', ], } }, { 'Action04' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action03', ], } }, { 'Action05' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action04', ], } }, { 'Action06' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action05', ], } }, { 'Action07' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action05', ], } }, { 'Action08' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action06', 'Action07' ], } }, { 'Action09' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action08', ], } }, { 'Action10' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action01a', 'Action02a' ], } }, { 'Action11' => { code_to_run => 'main::do_nothing', predecessors => [ 'Action08', ], } }, ]; return ($steps); } ## ---------- end sub define_steps_conf sub do_nothing { sleep int( rand(5) + 5.5 ); if ( int( rand() + 0.9 ) ) { # fail 10% of the time return ( { success => 1 } ); } else { return ( { success => 0, err_msg => "I screw up" } ); } } ## ---------- end sub do_nothing