File Coverage

lib/AnyEvent/Tools/RWMutex.pm
Criterion Covered Total %
statement 82 88 93.1
branch 26 38 68.4
condition 5 14 35.7
subroutine 13 15 86.6
pod 0 5 0.0
total 126 160 78.7


line stmt bran cond sub pod time code
1             package AnyEvent::Tools::RWMutex;
2 1     1   6 use Carp;
  1         2  
  1         75  
3 1     1   6 use AnyEvent::Util;
  1         1  
  1         139  
4              
5             sub new
6             {
7 3     3 0 6 my ($class) = @_;
8 3   33     47 return bless {
9             rlock => [],
10             wlock => [],
11             hno => 0,
12             rprocess => 0,
13             wprocess => 0,
14             cache => {},
15             rlock_limit => 0,
16             } => ref($class) || $class;
17             }
18              
19              
20             for my $m (qw(wlock rlock)) {
21 1     1   6 no strict 'refs';
  1         1  
  1         866  
22             * { __PACKAGE__ . "::$m" } = sub {
23 27     27   5942 my ($self, $cb) = @_;
24 27 50       76 croak "Usage: \$mutex->$m(sub { something })" unless 'CODE' eq ref $cb;
25              
26 27         57 my $name = $self->_add_client($m, $cb);
27 27         57 $self->_check_mutex;
28 27 50       82 return unless defined wantarray;
29 0 0       0 return unless keys %{ $self->{cache} };
  0         0  
30             return guard {
31 0 0 0 0   0 $self->_check_mutex if $self and $self->_delete_client($name)
32 0         0 };
33             }
34             }
35              
36             sub rlock_limit
37             {
38 117     117 0 158 my ($self, $value) = @_;
39 117 100       716 return $self->{rlock_limit} if @_ == 1;
40 1   50     6 return $self->{rlock_limit} = $value || 0;
41             }
42              
43             sub is_wlocked
44             {
45 72     72 0 101 my ($self) = @_;
46 72         236 return $self->{wprocess};
47             }
48              
49             sub is_rlocked
50             {
51 72     72 0 96 my ($self) = @_;
52 72         222 return $self->{rprocess};
53             }
54              
55             sub is_locked
56             {
57 0     0 0 0 my ($self) = @_;
58 0   0     0 return $self->is_wlocked || $self->is_rlocked;
59             }
60              
61             sub _add_client
62             {
63 27     27   38 my ($self, $queue, $cb) = @_;
64 27         50 my $name = ++$self->{hno};
65 27         27 $self->{cache}{$name} = [ $queue, scalar @{ $self->{$queue} } ];
  27         147  
66 27         33 push @{ $self->{$queue} }, [ $name, $cb ];
  27         84  
67 27         62 return $name;
68             }
69              
70             sub _delete_client
71             {
72 27     27   48 my ($self, $name) = @_;
73 27 50       108 return 0 unless exists $self->{cache}{$name};
74 27         45 my ($queue, $idx) = @{ delete $self->{cache}{$name} };
  27         112  
75              
76 27 100       71 if ($idx == $#{ $self->{$queue} }) {
  27         89  
77 9         10 pop @{ $self->{$queue} };
  9         15  
78 9         18 return 1;
79             }
80              
81 18         27 splice @{ $self->{$queue} }, $idx, 1;
  18         51  
82              
83 18         24 for (values %{ $self->{cache} }) {
  18         79  
84 154 50       280 next unless $_->[1] > $idx;
85 154 50       275 next unless $_->[0] eq $queue;
86 154         226 $_->[1]--;
87             }
88 18         41 return 1;
89             }
90              
91             sub _check_mutex
92             {
93 72     72   112 my ($self) = @_;
94 72 50       296 return if $self->is_wlocked;
95              
96 72         101 my $info;
97              
98 72 100       155 if ($self->is_rlocked) {
99 64 100       90 return if @{ $self->{wlock} };
  64         170  
100 61 100       177 return unless @{ $self->{rlock} };
  61         202  
101 57         225 goto LOCK_RMUTEX;
102             }
103              
104 8 100       12 if (@{ $self->{wlock} }) {
  8         30  
105 1         13 $info = $self->{wlock}[0];
106 1         8 $self->_delete_client($info->[0]);
107 1         3 $self->{wprocess}++;
108             my $guard = guard {
109 1 50   1   201229 if ($self) { # it can be already destroyed
110 1         4 $self->{wprocess}--;
111 1         8 $self->_check_mutex;
112             }
113 1         11 };
114 1         5 $info->[1]->($guard);
115 1         43 return;
116             }
117              
118 7 100       14 goto LOCK_RMUTEX if @{ $self->{rlock} };
  7         33  
119              
120 3         23 return;
121 61 100 100     140 LOCK_RMUTEX:
122             return if $self->rlock_limit
123             and $self->{rprocess} >= $self->rlock_limit;
124              
125 26         61 $info = $self->{rlock}[0];
126 26         81 $self->_delete_client($info->[0]);
127 26         43 $self->{rprocess}++;
128             my $guard = guard {
129 26 50   26   1554704 if ($self) { # it can be already destroyed
130 26         2772 $self->{rprocess}--;
131 26         947 $self->_check_mutex;
132             }
133 26         193 };
134 26         99 $info->[1]->($guard);
135 26 100       322 goto &_check_mutex if @{ $self->{rlock} };
  26         139  
136 8         25 return;
137             }
138              
139             1;