ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/SemaphoreSet.pm
Revision: 1.47
Committed: Sun May 25 03:05:42 2008 UTC (16 years ago) by root
Branch: MAIN
CVS Tags: rel-4_72
Changes since 1.46: +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 BEGIN { eval { require warnings } && warnings->unimport ("uninitialized") }
34
35 use Coro ();
36
37 $VERSION = 4.72;
38
39 =item new [inital count]
40
41 Creates a new semaphore 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 while () {
64 my $sem = ($_[0][1]{$_[1]} ||= [$_[0][0]]);
65
66 if ($sem->[0] > 0) {
67 --$sem->[0];
68 return 1;
69 }
70
71 push @{$sem->[1]}, $Coro::current;
72 &Coro::schedule;
73 }
74 }
75
76 sub timed_down {
77 require Coro::Timer;
78 my $timeout = Coro::Timer::timeout ($_[2]);
79
80 while () {
81 my $sem = ($_[0][1]{$_[1]} ||= [$_[0][0]]);
82
83 if ($sem->[0] > 0) {
84 --$sem->[0];
85 return 1;
86 }
87
88 if ($timeout) {
89 # ugly as hell.
90 for (0..$#{$sem->[1]}) {
91 if ($sem->[1][$_] == $Coro::current) {
92 splice @{$sem->[1]}, $_, 1;
93 return 0;
94 }
95 }
96 die;
97 }
98
99 push @{$sem->[1]}, $Coro::current;
100 &Coro::schedule;
101 }
102 }
103
104 =item $sem->up ($id)
105
106 Unlock the semaphore again.
107
108 =cut
109
110 sub up {
111 my $sem = $_[0][1]{$_[1]};
112
113 if (++$sem->[0] > 0) {
114 (shift @{$sem->[1]})->ready if @{$sem->[1]};
115 }
116
117 delete $_[0][1]{$_[1]} if $sem->[0] == $_[0][0] && !@{$sem->[1] || []};
118 }
119
120 =item $sem->try
121
122 Try to C<down> the semaphore. Returns true when this was possible,
123 otherwise return false and leave the semaphore unchanged.
124
125 =cut
126
127 sub try {
128 my $sem = ($_[0][1]{$_[1]} ||= [$_[0][0]]);
129 if ($sem->[0] > 0) {
130 --$sem->[0];
131 return 1;
132 } else {
133 return 0;
134 }
135 }
136
137 =item $sem->waiters ($id)
138
139 In scalar context, returns the number of coroutines waiting for this
140 semaphore.
141
142 =cut
143
144 sub waiters {
145 my $sem = $_[0][1]{$_[1]}
146 or return;
147 @{ $_[0][1]{$_[1]}[1] || []}
148 }
149
150 =item $guard = $sem->guard ($id)
151
152 This method calls C<down> and then creates a guard object. When the guard
153 object is destroyed it automatically calls C<up>.
154
155 =item $guard = $sem->timed_guard ($id, $timeout)
156
157 Like C<guard>, but returns undef if semaphore couldn't be acquired within
158 $timeout seconds, otherwise the guard object.
159
160 =cut
161
162 sub guard {
163 &down;
164 bless [@_], Coro::SemaphoreSet::guard::;
165 }
166
167 sub timed_guard {
168 &timed_down
169 ? bless [$_[0], $_[1]], Coro::SemaphoreSet::guard::
170 : ();
171 }
172
173 sub Coro::SemaphoreSet::guard::DESTROY {
174 &up(@{$_[0]});
175 }
176
177 1;
178
179 =back
180
181 =head1 AUTHOR
182
183 Marc Lehmann <schmorp@schmorp.de>
184 http://home.schmorp.de/
185
186 =cut
187