File Coverage

blib/lib/Object/Dependency.pm
Criterion Covered Total %
statement 128 188 68.0
branch 45 80 56.2
condition 33 89 37.0
subroutine 17 23 73.9
pod 10 15 66.6
total 233 395 58.9


line stmt bran cond sub pod time code
1              
2             package Object::Dependency;
3              
4 1     1   22434 use strict;
  1         3  
  1         42  
5 1     1   5 use warnings;
  1         1  
  1         35  
6 1     1   5 use Scalar::Util qw(refaddr blessed);
  1         7  
  1         149  
7 1     1   995 use Hash::Util qw(lock_keys);
  1         2939  
  1         7  
8 1     1   121 use Carp qw(confess);
  1         2  
  1         51  
9 1     1   1075 use List::MoreUtils qw(uniq);
  1         1272  
  1         84  
10 1     1   4180 use Data::Dumper; # XXX
  1         12596  
  1         2123  
11              
12             my $debug = 0;
13              
14             our $VERSION = 0.41;
15              
16             sub new
17             {
18 3     3 0 19 my ($pkg, %more) = @_;
19 3         16 my $self = {
20             addrmap => {}, # maps object id to object for non-stuck objects
21             independent => {}, # set of objects that have no dependencies
22             stuck => {}, # maps object id to object for stuck objects
23             %more,
24             };
25 3         8 bless $self, $pkg;
26 3         17 lock_keys(%$self);
27 3         33 return $self;
28             }
29              
30             sub newitem
31             {
32 33     33 0 38 my ($self, $i) = @_;
33 33   66     83 my $addr = refaddr($i) || $i;
34 33         184 my %item = (
35             dg_addr => $addr, # item number
36             dg_item => $i, # reference to object
37             dg_depends => {}, # items this depends upon
38             dg_blocks => {}, # items that depend upon this item
39             dg_active => 0, # item has been returned by independent() but not unlocked or removed
40             dg_lock => 0, # item is locked
41             dg_desc => undef, # description
42             dg_stuck => undef, # item is stuck, why it's stuck
43             );
44 33 50       61 return %item if wantarray;
45 33         60 my $o = bless \%item, 'Object::Dependency::Item';
46 33         74 lock_keys(%$o);
47 33         265 return $o;
48             }
49              
50             sub get_item
51             {
52 0     0 0 0 my ($self, $addr) = @_;
53 0   0     0 return $self->{addrmap}{$addr} || $self->{stuck}{$addr};
54             }
55              
56             sub get_addr
57             {
58 0     0 0 0 my ($self, $item) = @_;
59 0   0     0 my $addr = refaddr($item) || $item;
60 0 0 0     0 die unless $self->{addrmap}{$addr} || $self->{stuck}{$addr};
61 0         0 return $addr;
62             }
63              
64             sub unlock
65             {
66 1     1 0 3 my ($self, $item) = @_;
67 1   33     10 my $da = refaddr($item) || $item;
68 1 50 33     7 my $dao = $self->{addrmap}{$da} || $self->{stuck}{$da} or confess;
69 1         5 $dao->{dg_lock} = 0;
70             }
71              
72             sub add
73             {
74 42     42 1 169 my ($self, $item, @depends_upon) = @_;
75 42         55 for my $i ($item, @depends_upon) {
76 94   66     212 my $addr = refaddr($i) || $i;
77 94 100       232 next if $self->{addrmap}{$addr};
78 35 100       74 next if $self->{stuck}{$addr};
79 33         61 $self->{addrmap}{$addr} = $self->newitem($i);
80 33         66 $self->{independent}{$addr} = $self->{addrmap}{$addr};
81 33 50       66 printf STDERR "ADD ITEM %s\n", $self->desc($addr) if $debug;
82             };
83 42   66     111 my $da = refaddr($item) || $item;
84 42   66     94 my $dao = $self->{addrmap}{$da} || $self->{stuck}{$da};
85 42 100       92 delete $self->{independent}{$da}
86             if @depends_upon;
87 42         60 for my $d (@depends_upon) {
88 52   33     128 my $addr = refaddr($d) || $d;
89 52   66     114 my $o = $self->{addrmap}{$addr} || $self->{stuck}{$addr};
90 52         86 $o->{dg_blocks}{$da} = $dao;
91 52         89 $dao->{dg_depends}{$addr} = $o;
92 52 100       179 $self->stuck_dependency($da, "Stuck on " . $self->desc($o))
93             if $self->{stuck}{$addr};
94             }
95             }
96              
97             sub remove_all_dependencies
98             {
99 0     0 1 0 my ($self, @items) = @_;
100 0         0 my (@remove);
101 0         0 for my $i (@items) {
102 0   0     0 my $addr = refaddr($i) || $i;
103 0   0     0 my $o = $self->{addrmap}{$addr} || $self->{stuck}{$addr};
104 0         0 for my $ubi (keys %{$o->{dg_blocks}}) {
  0         0  
105 0         0 my $unblock = delete $o->{dg_blocks}{$ubi};
106 0         0 delete $unblock->{dg_depends}{$addr};
107 0         0 $self->remove_all_dependencies($unblock);
108 0         0 push(@remove, $unblock);
109 0 0       0 next if keys %{$unblock->{dg_depends}};
  0         0  
110 0 0       0 next if $unblock->{dg_stuck};
111 0         0 $self->{independent}{$unblock->{dg_addr}} = $unblock;
112             }
113             }
114 0 0 0     0 $self->remove_dependency(grep { $self->{addrmap}{refaddr($_) || $_} || $self->{stuck}{refaddr($_) || $_} } uniq @remove);
  0   0     0  
115             }
116              
117             sub is_dependency
118             {
119 0     0 1 0 my ($self, $item) = @_;
120 0   0     0 my $addr = refaddr($item) || $item;
121 0   0     0 return defined($self->{addrmap}{$addr} || $self->{stuck}{$addr});
122             }
123              
124             sub remove_dependency
125             {
126 13     13 1 9806 my ($self, @items) = @_;
127 13         23 for my $i (@items) {
128 20   66     63 my $addr = refaddr($i) || $i;
129 20 50       41 if ($debug) {
130 0         0 my($p,$f,$l) = caller;
131 0 0       0 printf STDERR "REMOVE ITEM %s:%d: %s %s\n", $f, $l, $self->desc($addr), ($i->{desc} ? $i->{desc} : ($i->{trace} ? $i->{trace} : "$i"));
    0          
132             }
133 20         41 delete $self->{independent}{$addr};
134              
135             # we won't complain about removing stuck dependencies
136 20 50 33     67 my $o = delete($self->{addrmap}{$addr}) || delete($self->{stuck}{$addr}) or confess;
137              
138 20 50       21 if (keys %{$o->{dg_depends}}) {
  20         46  
139 0         0 printf STDERR "attempting to remove %s but it has dependencies that aren't met:\n", $self->desc($o);
140 0         0 for my $da (keys %{$o->{dg_depends}}) {
  0         0  
141 0         0 printf STDERR "\t%s\n", $self->desc($da);
142             }
143 0         0 die "fatal error";
144             }
145 20         21 for my $unblock (values %{$o->{dg_blocks}}) {
  20         72  
146 32         55 delete $unblock->{dg_depends}{$addr};
147 32         35 $unblock->{dg_active} = 0;
148 32 100       32 next if keys %{$unblock->{dg_depends}};
  32         86  
149 19 100       40 next if $unblock->{dg_stuck};
150 18         71 $self->{independent}{$unblock->{dg_addr}} = $unblock;
151             }
152             }
153             }
154              
155             sub stuck_dependency
156             {
157 6     6 1 10 my ($self, $item, $problem) = @_;
158 6   66     47 my $addr = refaddr($item) || $item;
159 6   33     19 my $o = $self->{addrmap}{$addr} || $self->{stuck}{$addr};
160 6 50       12 return if $o->{dg_stuck};
161 6 50       19 confess unless blessed $o;
162 6   66     29 $o->{dg_stuck} = $problem || sprintf("stuck called from %s line %d", (caller())[1,2]);
163 6         13 $self->{stuck}{$addr} = $o;
164 6         8 delete $self->{independent}{$addr};
165 6         11 delete $self->{addrmap}{$addr};
166 6         6 for my $also_stuck (keys %{$o->{dg_blocks}}) {
  6         24  
167 3         8 $self->stuck_dependency($also_stuck, "Stuck on " . $self->desc($addr));
168             }
169             }
170              
171             sub independent
172             {
173 28     28 1 3763 my ($self, %opts) = @_;
174              
175 28   50     109 my $count = $opts{count} || 0;
176 28   100     73 my $active = $opts{active} || 0;
177 28   100     73 my $lock = $opts{lock} || 0;
178 28   100     70 my $stuck = $opts{stuck} || 0;
179              
180 28         29 my @ind;
181 28 100       32 for my $o (values %{$self->{$stuck ? 'stuck' : 'independent'}}) {
  28         83  
182 47 100 66     114 next if $active && $o->{dg_active};
183 46 100       84 next if $o->{dg_lock};
184 44         59 push(@ind, $o->{dg_item});
185 44         48 $o->{dg_active} = 1;
186 44         48 $o->{dg_lock} = $lock;
187 44 50 33     102 last if $count && @ind == $count;
188             }
189 28 100       110 return @ind if @ind;
190 6 100       6 return () if keys %{$self->{independent}};
  6         23  
191 3 50       5 return () unless keys %{$self->{addrmap}};
  3         11  
192 0         0 confess "No independent objects, but there are still objects in the dependency graph:\n" . $self->dump_graph_string();
193             }
194              
195             sub alldone
196             {
197 5     5 1 952 my ($self) = @_;
198 5 100       6 return 0 if keys %{$self->{independent}};
  5         21  
199 3 50       4 return 0 if keys %{$self->{addrmap}};
  3         8  
200 3         11 return 1;
201             }
202              
203             sub desc
204             {
205 4     4 1 9 my ($self, $addr, $desc) = @_;
206 4         4 my $o;
207 4 100       8 if (ref($addr)) {
208 1         2 $o = $addr;
209 1   33     3 $addr = refaddr($addr) || $addr;
210             } else {
211 3   33     16 $o = $self->{addrmap}{$addr} || $self->{stuck}{$addr};
212             }
213 4 50       8 return "NO SUCH OBJECT $addr" unless $o;
214 4         6 my $node = $o->{dg_item};
215 4 50       22 $o->{dg_desc} = $desc
216             if defined $desc;
217 4         6 $desc = '';
218 4 50       12 $desc .= 'INDEPENDENT ' if $self->{independent}{$addr};
219 4 50       10 $desc .= 'LOCKED ' if $o->{dg_lock};
220 4 50       8 $desc .= 'ACTIVE ' if $o->{dg_lock};
221 4         9 $desc .= "$addr ";
222 4 50       13 if ($o->{dg_desc}) {
    50          
223 0         0 $desc .= $o->{dg_desc};
224             } elsif (blessed($node)) {
225 0 0       0 if ($node->isa('Proc::JobQueue::Job')) {
    0          
226 1     1   12 no warnings;
  1         2  
  1         536  
227 0         0 $desc .= "JOB$node->{jobnum} $node->{status} $node->{desc}";
228             } elsif ($node->isa('Proc::JobQueue::DependencyTask')) {
229 0         0 $desc .= "TASK $node->{desc}";
230             } else {
231 0         0 die;
232             }
233             } else {
234 4         8 $desc .= "???????????????????";
235             }
236 4 50       14 $desc .= " STUCK: $o->{dg_stuck}" if $o->{dg_stuck};
237 4         20 return $desc;
238             }
239              
240             sub dump_graph
241             {
242 0     0 1   my ($self) = @_;
243 0           print $self->dump_graph_string();
244             }
245              
246             sub dump_graph_string
247             {
248 0     0 1   my ($self) = @_;
249              
250 0           my $r = sprintf "Dependency Graph, alldone=%d\n", $self->alldone;
251 0           my %desc;
252 0           for my $addr (sort (keys %{$self->{addrmap}}, keys %{$self->{stuck}})) {
  0            
  0            
253 0           $desc{$addr} = $self->desc($addr);
254             }
255 0           for my $addr (sort (keys %{$self->{addrmap}}, keys %{$self->{stuck}})) {
  0            
  0            
256 0           $r .= "\t$desc{$addr}\n";
257 0   0       my $node = $self->{addrmap}{$addr} || $self->{stuck}{$addr};
258 0           for my $b (keys %{$node->{dg_blocks}}) {
  0            
259 0           $r .= "\t\tBLOCKS\t$desc{$b}\n";
260             }
261 0           for my $d (keys %{$node->{dg_depends}}) {
  0            
262 0           $r .= "\t\tDEP_ON\t$desc{$d}\n";
263             }
264             }
265 0           return $r;
266             }
267              
268             ;
269              
270             __END__