File Coverage

blib/lib/Proc/tored/Machine.pm
Criterion Covered Total %
statement 69 72 95.8
branch n/a
condition n/a
subroutine 24 25 96.0
pod 0 4 0.0
total 93 101 92.0


line stmt bran cond sub pod time code
1             package Proc::tored::Machine;
2             $Proc::tored::Machine::VERSION = '0.18';
3 2     2   17 use strict;
  2         5  
  2         184  
4 2     2   137 use warnings;
  2         8  
  2         75  
5 2     2   15 use Moo;
  2         6  
  2         14  
6 2     2   842 use Carp;
  2         9  
  2         182  
7 2     2   1197 use Auto::Mata '!with';
  2         331817  
  2         455  
8 2     2   22 use Proc::tored::Flag;
  2         5  
  2         65  
9 2     2   985 use Proc::tored::PidFile;
  2         12  
  2         123  
10 2     2   1264 use Proc::tored::Types -types;
  2         11  
  2         40  
11 2     2   2130 use Time::HiRes;
  2         116  
  2         24  
12 2     2   271 use Type::Utils qw(declare as where);
  2         7  
  2         16  
13 2     2   1991 use Types::Standard -types;
  2         7  
  2         19  
14              
15 2     2   15874 use constant READY => 'READY';
  2         7  
  2         179  
16 2     2   17 use constant STATUS => 'STATUS';
  2         6  
  2         142  
17 2     2   17 use constant LOCK => 'LOCK';
  2         8  
  2         123  
18 2     2   15 use constant TOUCH => 'TOUCH';
  2         8  
  2         128  
19 2     2   17 use constant STOP => 'STOP';
  2         7  
  2         119  
20 2     2   15 use constant TERM => 'TERM';
  2         6  
  2         3631  
21              
22             my $Lock = Maybe[InstanceOf['Guard']];
23             my $Flag = InstanceOf['Proc::tored::Flag'];
24             my $PidFile = InstanceOf['Proc::tored::PidFile'];
25              
26             my $Proctor = declare 'Proctor', as Dict[
27               pidfile => $PidFile, # PidFile
28               stopped => $Flag, # Stop Flag
29               paused => $Flag, # Pause Flag
30               traps => SignalList, # list of signals to trap
31               call => CodeRef, # code ref to call while running
32               lock => $Lock, # guard set on successful lock
33               started => Bool, # initialization status
34               quit => Bool, # flag set by trapped posix signals
35               finish => Bool, # true when last callback returned false
36             ];
37              
38             my $Stopped = declare 'Stopped', as $Proctor, where { $_->{stopped}->is_set || $_->{quit} };
39             my $NotStopped = declare 'NotStopped', as ~$Stopped;
40             my $Unlocked = declare 'Unlocked', as $NotStopped, where { !$_->{lock} };
41             my $Locked = declare 'Locked', as $NotStopped, where { $_->{lock} };
42             my $Started = declare 'Started', as $Locked, where { $_->{started} };
43             my $Paused = declare 'Paused', as $Started, where { $_->{paused}->is_set };
44             my $NotPaused = declare 'NotPaused', as ~$Paused;
45             my $Running = declare 'Running', as $NotPaused & $Started, where { !$_->{finish} };
46             my $Finished = declare 'Finished', as $Started, where { $_->{finish} };
47              
48             sub pause_sleep {
49 0     0 0 0   my ($acc, $time) = @_;
50 0         0   Time::HiRes::sleep($time);
51 0         0   $acc;
52             }
53              
54             sub sigtrap {
55 8     8 0 27   my $acc = shift;
56 8         21   foreach my $signal (@{$acc->{traps}}) {
  8         105  
57                 $SIG{$signal} = sub {
58 2     2   619       warn "Caught SIG$signal\n";
59 2         21       $acc->{quit} = 1;
60 2         11       $acc;
61 2         72     };
62               }
63             }
64              
65             my $FSM = machine {
66               ready READY;
67               term TERM;
68              
69             # Ready
70               transition READY, to STATUS, on $Proctor;
71              
72             # Service loop
73               transition STATUS, to STOP, on $Finished;
74               transition STATUS, to STOP, on $Stopped;
75               transition STATUS, to TOUCH, on $Paused, using { pause_sleep($_, 0.2) };
76               transition STATUS, to TOUCH, on $Running, using { $_->{finish} = $_->{call}->() ? 0 : 1; $_ };
77              
78             # Touch pid file
79               transition TOUCH, to STATUS, using { $_->{pidfile}->touch; $_ };
80              
81             # PidFile lock
82               transition STATUS, to LOCK, on $Unlocked, using { $_->{lock} = $_->{pidfile}->lock; $_ };
83               transition LOCK, to STATUS, on $Locked, using { sigtrap($_); $_->{started} = 1; $_ };
84               transition LOCK, to TERM, on $Unlocked;
85              
86             # Term
87               transition STOP, to TERM, on $Proctor, using { undef $_->{lock}; undef $SIG{$_} foreach @{$_->{traps}}; $_ };
88             };
89              
90             has pidfile_path => (is => 'ro', isa => NonEmptyStr, required => 1);
91             has stop_path => (is => 'ro', isa => NonEmptyStr, required => 1);
92             has pause_path => (is => 'ro', isa => NonEmptyStr, required => 1);
93             has traps => (is => 'ro', isa => SignalList, default => sub {[]});
94              
95             has pidfile => (
96               is => 'lazy',
97               isa => $PidFile,
98               handles => {
99                 read_pid => 'read_file',
100                 running_pid => 'running_pid',
101                 is_running => 'is_running',
102               },
103             );
104              
105             has stop_flag => (
106               is => 'lazy',
107               isa => $Flag,
108               handles => {
109                 stop => 'set',
110                 start => 'unset',
111                 is_stopped => 'is_set',
112               },
113             );
114              
115             has pause_flag => (
116               is => 'lazy',
117               isa => $Flag,
118               handles => {
119                 pause => 'set',
120                 resume => 'unset',
121                 is_paused => 'is_set',
122               },
123             );
124              
125 5     5   327 sub _build_pidfile { Proc::tored::PidFile->new(file_path => shift->pidfile_path) }
126 5     5   395 sub _build_stop_flag { Proc::tored::Flag->new(touch_file_path => shift->stop_path) }
127 5     5   1309 sub _build_pause_flag { Proc::tored::Flag->new(touch_file_path => shift->pause_path) }
128              
129             sub clear_flags {
130 11     11 0 26338   my $self = shift;
131 11         318   $self->start;
132 11         2007   $self->resume;
133             }
134              
135             sub run {
136 9     9 0 144   my ($self, $code) = @_;
137              
138 9         241   my $acc = {
139                 pidfile => $self->pidfile,
140                 stopped => $self->stop_flag,
141                 paused => $self->pause_flag,
142                 traps => $self->traps,
143                 call => $code,
144                 lock => undef,
145                 started => 0,
146                 finish => 0,
147                 quit => 0,
148               };
149              
150 9         1205   my $service = $FSM->();
151              
152 9         244   $service->($acc);
153              
154 9         731   return $acc->{started};
155             };
156              
157             1;
158              
159             __END__
160            
161             =pod
162            
163             =encoding UTF-8
164            
165             =head1 NAME
166            
167             Proc::tored::Machine
168            
169             =head1 VERSION
170            
171             version 0.18
172            
173             =head1 AUTHOR
174            
175             Jeff Ober <sysread@fastmail.fm>
176            
177             =head1 COPYRIGHT AND LICENSE
178            
179             This software is copyright (c) 2017 by Jeff Ober.
180            
181             This is free software; you can redistribute it and/or modify it under
182             the same terms as the Perl 5 programming language system itself.
183            
184             =cut
185