File Coverage

lib/AnyEvent/Tools/Mutex.pm
Criterion Covered Total %
statement 53 53 100.0
branch 14 20 70.0
condition 2 6 33.3
subroutine 10 10 100.0
pod 0 3 0.0
total 79 92 85.8


line stmt bran cond sub pod time code
1             package AnyEvent::Tools::Mutex;
2 1     1   6 use Carp;
  1         3  
  1         79  
3 1     1   6 use AnyEvent::Util;
  1         1  
  1         562  
4              
5             sub new
6             {
7 3     3 0 8 my ($class) = @_;
8 3   33     41 return bless {
9             queue => [],
10             cache => {},
11             hno => 0,
12             process => 0,
13             } => ref($class) || $class;
14             }
15              
16             sub lock
17             {
18 7     7 0 200686 my ($self, $cb) = @_;
19 7 50       27 croak 'Usage: $mutex->lock(sub { something })' unless 'CODE' eq ref $cb;
20              
21 7         21 my $name = $self->_add_client($cb);
22 7         21 $self->_check_mutex;
23 7 100       66 return unless defined wantarray;
24 1 50       2 return unless keys %{ $self->{cache} };
  1         6  
25             return guard {
26 1 50 33 1   48341 $self->_check_mutex if $self and $self->_delete_client($name)
27 1         8 };
28             }
29              
30             sub is_locked
31             {
32 40     40 0 4802696 my ($self) = @_;
33 40         454 return $self->{process};
34             }
35              
36             sub _add_client
37             {
38 7     7   12 my ($self, $cb) = @_;
39 7         16 my $name = ++$self->{hno};
40 7         35 $self->{cache}{$name} = @{ $self->{queue} };
  7         23  
41 7         13 push @{ $self->{queue} }, [ $name, $cb ];
  7         23  
42 7         21 return $name;
43             }
44              
45             sub _delete_client
46             {
47 7     7   16 my ($self, $name) = @_;
48 7 50       38 return 0 unless exists $self->{cache}{$name};
49 7         28 my $idx = delete $self->{cache}{$name};
50 7 100       14 if ($idx == $#{ $self->{queue} }) {
  7         31  
51 5         10 pop @{ $self->{queue} };
  5         8  
52 5         13 return 1;
53             }
54              
55 2         6 splice @{ $self->{queue} }, $idx, 1;
  2         8  
56 2         16 for (values %{ $self->{cache} }) {
  2         11  
57 2 50       10 next unless $_ > $idx;
58 2         6 $_--;
59             }
60 2         15 return 1;
61             }
62              
63             sub _check_mutex
64             {
65 14     14   34 my ($self) = @_;
66 14 100       45 return if $self->is_locked;
67 9 100       19 return unless @{ $self->{queue} };
  9         105  
68 6         14 $self->{process}++;
69 6         17 my $info = $self->{queue}[0];
70 6         35 $self->_delete_client($info->[0]);
71             my $guard = guard {
72 6 50   6   352211 if ($self) { # it can be aleady destroyed
73 6         25 $self->{process}--;
74 6         41 $self->_check_mutex;
75             }
76 6         67 };
77 6         27 $info->[1]->($guard);
78             }
79              
80             1;