File Coverage

blib/lib/IPC/Manager/Client.pm
Criterion Covered Total %
statement 97 137 70.8
branch 30 62 48.3
condition 13 28 46.4
subroutine 17 38 44.7
pod 28 30 93.3
total 185 295 62.7


line stmt bran cond sub pod time code
1             package IPC::Manager::Client;
2 2     2   1066 use strict;
  2         4  
  2         108  
3 2     2   12 use warnings;
  2         5  
  2         174  
4              
5             our $VERSION = '0.000005';
6              
7 2     2   13 use Carp qw/croak/;
  2         4  
  2         128  
8 2     2   11 use Errno qw/ESRCH/;
  2         3  
  2         357  
9 2     2   19 use Scalar::Util qw/blessed weaken/;
  2         4  
  2         143  
10              
11 2     2   977 use IPC::Manager::Message;
  2         7  
  2         87  
12              
13 2         7 use Object::HashBase qw{
14            
15            
16            
17            
18            
19             +reconnect
20            
21 2     2   11 };
  2         4  
22              
23             my ($PID, @LOCAL);
24              
25             sub local_clients {
26 2     2 1 4 my $class = shift;
27 2         6 my ($route) = @_;
28              
29 2 50       8 croak "'route' is required" unless $route;
30              
31 2 50       7 return unless $PID;
32 2 50       14 if ($PID != $$) {
33 0         0 $PID = $$;
34 0         0 return @LOCAL = ();
35             }
36              
37 2 50       5 return grep { $_ && $_->route eq $route } @LOCAL;
  8         50  
38             }
39              
40       0 1   sub unspawn { }
41       0 1   sub pre_disconnect_hook { }
42       0 1   sub pre_suspend_hook { }
43       0 1   sub post_suspend_hook { }
44       8 1   sub post_disconnect_hook { }
45              
46 0     0 1 0 sub reconnect { shift->connect(@_, reconnect => 1) }
47 102 50   102 1 433 sub pid_check { croak "Client used from wrong PID" if $_[0]->{+PID} != $$; $_[0] }
  102         157  
48              
49 0     0 1 0 sub have_pending_messages { 0 }
50 0     0 1 0 sub have_ready_messages { croak "Not Implemented" }
51 0     0 0 0 sub handles_for_select { croak "Not Implemented" }
52              
53 0     0 1 0 sub get_messages { croak "Not Implemented" }
54 0     0 1 0 sub peer_exists { croak "Not Implemented" }
55 0     0 1 0 sub peer_pid { croak "Not Implemented" }
56 0     0 1 0 sub peers { croak "Not Implemented" }
57 0     0 1 0 sub read_stats { croak "Not Implemented" }
58 0     0 1 0 sub send_message { croak "Not Implemented" }
59 0     0 1 0 sub spawn { croak "Not Implemented" }
60 0     0 1 0 sub write_stats { croak "Not Implemented" }
61 0     0 1 0 sub all_stats { croak "Not Implemented" }
62              
63             sub pid_is_running {
64 0     0 1 0 my $pid = pop;
65              
66 0 0       0 croak "A pid is required" unless $pid;
67              
68 0         0 local $!;
69              
70 0 0       0 return 1 if kill(0, $pid); # Running and we have perms
71 0 0       0 return 0 if $! == ESRCH; # Does not exist (not running)
72 0         0 return -1; # Running, but not ours
73             }
74              
75             sub connect {
76 8     8 1 18 my $class = shift;
77 8         23 my ($id, $serializer, $route, %params) = @_;
78 8         60 return $class->new(%params, SERIALIZER() => $serializer, ROUTE() => $route, ID() => $id);
79             }
80              
81             sub init {
82 8     8 0 12 my $self = shift;
83              
84 8 100 66     81 if (!$PID || $PID != $$) {
85 2         18 $PID = $$;
86 2         4 @LOCAL = ();
87             }
88              
89 8         17 push @LOCAL => $self;
90 8         17 weaken($LOCAL[-1]);
91              
92 8 50       23 croak "'serializer' is a required attribute" unless $self->{+SERIALIZER};
93 8 50       24 croak "'route' is a required attribute" unless $self->{+ROUTE};
94              
95 8   33     30 my $id = $self->{+ID} // croak "'id' is a required attribute";
96              
97 8 50       28 croak "'id' may not begin with an underscore" if $id =~ m/^_/;
98              
99 8   33     47 $self->{+PID} //= $$;
100 8 50       32 $self->{+STATS} = $self->read_stats if $self->{+RECONNECT};
101 8   50     59 $self->{+STATS} //= {read => {}, sent => {}};
102             }
103              
104             sub build_message {
105 26     26 1 35 my $self = shift;
106 26 50       119 my $in = @_ % 2 ? shift(@_) : undef;
107 26 100 66     118 if (@_ == 2 && $_[1] ne 'content') {
108 8         33 @_ = (to => $_[0], content => $_[1]);
109             }
110 26 50       179 return IPC::Manager::Message->new(($in ? %$in : ()), from => $self->{+ID}, @_);
111             }
112              
113             sub broadcast {
114 8     8 1 99 my $self = shift;
115              
116 8 50 33     53 if (@_ == 1 && !(blessed($_[0]) && $_[0]->isa('IPC::Manager::Message'))) {
      33        
117 8         25 @_ = (content => $_[0]);
118             }
119              
120 8         13 my %out;
121 8         34 for my $peer ($self->peers) {
122 18         77 my ($ok, $err) = $self->try_message(@_, to => $peer, broadcast => 1, id => undef);
123 18 50       78 $out{$peer} = $ok ? {sent => 1} : {sent => 0, error => $err};
124             }
125              
126 8         37 return \%out;
127             }
128              
129             sub try_message {
130 18     18 1 28 my $self = shift;
131 18         38 my $args = \@_;
132              
133 18         30 my ($ok, $err);
134             {
135 18         24 local $@;
  18         24  
136 18 50       35 if (eval { $self->send_message(@$args); 1 }) {
  18         56  
  18         56  
137 18         39 $ok = 1;
138             }
139             else {
140 0         0 $ok = 0;
141 0   0     0 $err = $@ // "unknown error";
142             }
143             }
144              
145 18 50       94 return ($ok, $err) if wantarray;
146              
147 0         0 $@ = $err;
148 0         0 return $ok;
149             }
150              
151             sub requeue_message {
152 0     0 1 0 my $self = shift;
153 0         0 $self->send_message(@_, to => $self->{+ID});
154             }
155              
156             sub peer_active {
157 0     0 1 0 my $self = shift;
158              
159 0         0 my $peer_pid = $self->peer_pid(@_);
160              
161 0 0       0 return 0 unless $peer_pid;
162 0 0       0 return 0 unless $self->pid_is_running($peer_pid);
163 0 0       0 return 0 unless kill(0, $peer_pid);
164 0         0 return 1;
165             }
166              
167             sub disconnect {
168 16     16 1 41 my $self = shift;
169 16         35 my ($handler) = @_;
170              
171 16         70 $self->pid_check;
172              
173 16 100       45 return if $self->{+DISCONNECTED};
174              
175 8         31 $self->pre_disconnect_hook;
176              
177             # Wait for any messages that are still being written
178 8         17 my $err;
179 8   100     34 while ($self->pending_messages || $self->ready_messages) {
180 6 50       175 if (my @ready = $self->get_messages) {
181 6         15 @ready = grep { !$_->is_terminate } @ready;
  8         23  
182 6 100       33 if (@ready) {
183 2 50       12 if ($handler) {
184 0         0 $self->$handler(\@ready);
185             }
186             else {
187 2         12 $self->{+STATS}->{read}->{$_->{from}}-- for @ready;
188 2         7 $err = "Messages waiting at disconnect for $self->{+ID}";
189 2         24 last;
190             }
191             }
192             }
193             }
194              
195 8         36 $self->{+DISCONNECTED} = 1;
196              
197 8         62 $self->post_disconnect_hook;
198              
199 8         63 $self->write_stats;
200              
201 8 100       567 croak $err if $err;
202             }
203              
204             sub suspend {
205 0     0 1 0 my $self = shift;
206 0         0 $self->pid_check;
207              
208 0         0 $self->pre_suspend_hook;
209              
210 0         0 $self->{+DISCONNECTED} = 1;
211              
212 0         0 $self->post_suspend_hook;
213 0         0 $self->write_stats;
214             }
215              
216             sub DESTROY {
217 8     8   5067 my $self = shift;
218 8 50 33     72 return unless $self->{+PID} && $self->{+PID} == $$;
219 8         14 local $@;
220 8 50       15 eval { $self->disconnect; 1 } or warn $@;
  8         26  
  8         18  
221 8 50       14 eval { $self->write_stats; 1 } or warn $@;
  8         23  
  8         758  
222             }
223              
224             1;
225              
226             __END__