ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/SemaphoreSet.pm
Revision: 1.13
Committed: Sat Feb 9 18:53:03 2002 UTC (22 years, 3 months ago) by root
Branch: MAIN
Changes since 1.12: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Coro::SemaphoreSet - hash of semaphores.
4
5 =head1 SYNOPSIS
6
7 use Coro::SemaphoreSet;
8
9 $sig = new Coro::SemaphoreSet [initial value];
10
11 $sig->down("semaphoreid"); # wait for signal
12
13 # ... some other "thread"
14
15 $sig->up("semaphoreid");
16
17 =head1 DESCRIPTION
18
19 This module implements sets of counting semaphores (see
20 L<Coro::Semaphore>). It is nothing more than a hash with normal semaphores
21 as members, but is more efficiently managed.
22
23 This is useful if you want to allow parallel tasks to run in parallel but
24 not on the same problem. Just use a SemaphoreSet and lock on the problem
25 identifier.
26
27 =over 4
28
29 =cut
30
31 package Coro::SemaphoreSet;
32
33 no warnings qw(uninitialized);
34
35 use Coro ();
36
37 $VERSION = 0.532;
38
39 =item new [inital count]
40
41 Creates a new sempahore set with the given initial lock count for each
42 individual semaphore. See L<Coro::Semaphore>.
43
44 =cut
45
46 sub new {
47 bless [defined $_[1] ? $_[1] : 1], $_[0];
48 }
49
50 =item $sem->down($id)
51
52 Decrement the counter, therefore "locking" the named semaphore. This
53 method waits until the semaphore is available if the counter is zero.
54
55 =item $status = $sem->timed_down($id, $timeout)
56
57 Like C<down>, but returns false if semaphore couldn't be acquired within
58 $timeout seconds, otherwise true.
59
60 =cut
61
62 sub down {
63 my $sem = ($_[0][1]{$_[1]} ||= [$_[0][0]]);
64 while ($sem->[0] <= 0) {
65 push @{$sem->[1]}, $Coro::current;
66 Coro::schedule;
67 }
68 --$sem->[0];
69 }
70
71 sub timed_down {
72 require Coro::Timer;
73 my $timeout = Coro::Timer::timeout($_[2]);
74
75 my $sem = ($_[0][1]{$_[1]} ||= [$_[0][0]]);
76 while ($sem->[0] <= 0) {
77 push @{$sem->[1]}, $Coro::current;
78 Coro::schedule;
79 if ($timeout) {
80 # ugly as hell.
81 for (0..$#{$sem->[1]}) {
82 if ($sem->[1][$_] == $Coro::current) {
83 splice @{$sem->[1]}, $_, 1;
84 return;
85 }
86 }
87 die;
88 }
89 }
90 --$sem->[0];
91 return 1;
92 }
93
94 =item $sem->up($id)
95
96 Unlock the semaphore again.
97
98 =cut
99
100 sub up {
101 my $sem = $_[0][1]{$_[1]};
102 if (++$sem->[0] > 0) {
103 (shift @{$sem->[1]})->ready if @{$sem->[1]};
104 }
105 delete $_[0][1]{$_[1]} if $sem->[0] == $_[0][0];
106 }
107
108 =item $sem->try
109
110 Try to C<down> the semaphore. Returns true when this was possible,
111 otherwise return false and leave the semaphore unchanged.
112
113 =cut
114
115 sub try {
116 my $sem = ($_[0][1]{$_[1]} ||= [$_[0][0]]);
117 if ($sem->[0] > 0) {
118 --$sem->[0];
119 return 1;
120 } else {
121 return 0;
122 }
123 }
124
125 =item $sem->waiters($id)
126
127 In scalar context, returns the number of coroutines waiting for this
128 semaphore.
129
130 =cut
131
132 sub waiters {
133 @{$_[0][1]{$_[1]}};
134 }
135
136 =item $guard = $sem->guard($id)
137
138 This method calls C<down> and then creates a guard object. When the guard
139 object is destroyed it automatically calls C<up>.
140
141 =item $guard = $sem->timed_guard($id, $timeout)
142
143 Like C<guard>, but returns undef if semaphore couldn't be acquired within
144 $timeout seconds, otherwise the guard object.
145
146 =cut
147
148 sub guard {
149 &down;
150 bless [@_], Coro::SemaphoreSet::guard::;
151 }
152
153 sub guard {
154 &timed_down
155 ? bless [$_[0], $_[1]], Coro::SemaphoreSet::guard::
156 : ();
157 }
158
159 sub Coro::SemaphoreSet::guard::DESTROY {
160 &up(@{$_[0]});
161 }
162
163 1;
164
165 =back
166
167 =head1 AUTHOR
168
169 Marc Lehmann <pcg@goof.com>
170 http://www.goof.com/pcg/marc/
171
172 =cut
173