ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Semaphore.pm
Revision: 1.42
Committed: Wed Jan 12 20:35:35 2005 UTC (19 years, 4 months ago) by root
Branch: MAIN
Changes since 1.41: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Coro::Semaphore - non-binary semaphores
4
5 =head1 SYNOPSIS
6
7 use Coro::Semaphore;
8
9 $sig = new Coro::Semaphore [initial value];
10
11 $sig->down; # wait for signal
12
13 # ... some other "thread"
14
15 $sig->up;
16
17 =head1 DESCRIPTION
18
19 This module implements counting semaphores. You can initialize a mutex
20 with any level of parallel users, that is, you can intialize a sempahore
21 that can be C<down>ed more than once until it blocks. There is no owner
22 associated with semaphores, so one coroutine can C<down> it while another
23 can C<up> it.
24
25 Counting semaphores are typically used to coordinate access to
26 resources, with the semaphore count initialized to the number of free
27 resources. Coroutines then increment the count when resources are added
28 and decrement the count when resources are removed.
29
30 =over 4
31
32 =cut
33
34 package Coro::Semaphore;
35
36 BEGIN { eval { require warnings } && warnings->unimport ("uninitialized") }
37
38 use Coro ();
39
40 $VERSION = 1.0;
41
42 =item new [inital count]
43
44 Creates a new sempahore object with the given initial lock count. The
45 default lock count is 1, which means it is unlocked by default. Zero (or
46 negative values) are also allowed, in which case the semaphore is locked
47 by default.
48
49 =cut
50
51 sub new {
52 bless [defined $_[1] ? $_[1] : 1], $_[0];
53 }
54
55 =item $sem->down
56
57 Decrement the counter, therefore "locking" the semaphore. This method
58 waits until the semaphore is available if the counter is zero.
59
60 =item $status = $sem->timed_down($timeout)
61
62 Like C<down>, but returns false if semaphore couldn't be acquired within
63 $timeout seconds, otherwise true.
64
65 =cut
66
67 sub down {
68 while ($_[0][0] <= 0) {
69 push @{$_[0][1]}, $Coro::current;
70 Coro::schedule;
71 }
72 --$_[0][0];
73 }
74
75 sub timed_down {
76 require Coro::Timer;
77 my $timeout = Coro::Timer::timeout($_[1]);
78
79 while ($_[0][0] <= 0) {
80 push @{$_[0][1]}, $Coro::current;
81 Coro::schedule;
82 if ($timeout) {
83 # ugly as hell. slow, too, btw!
84 for (0..$#{$_[0][1]}) {
85 if ($_[0][1][$_] == $Coro::current) {
86 splice @{$_[0][1]}, $_, 1;
87 return;
88 }
89 }
90 die;
91 }
92 }
93
94 --$_[0][0];
95 return 1;
96 }
97
98 =item $sem->up
99
100 Unlock the semaphore again.
101
102 =cut
103
104 sub up {
105 if (++$_[0][0] > 0) {
106 (shift @{$_[0][1]})->ready if @{$_[0][1]};
107 }
108 }
109
110 =item $sem->try
111
112 Try to C<down> the semaphore. Returns true when this was possible,
113 otherwise return false and leave the semaphore unchanged.
114
115 =cut
116
117 sub try {
118 if ($_[0][0] > 0) {
119 --$_[0][0];
120 return 1;
121 } else {
122 return 0;
123 }
124 }
125
126 =item $sem->waiters
127
128 In scalar context, returns the number of coroutines waiting for this
129 semaphore.
130
131 =cut
132
133 sub waiters {
134 @{$_[0][1]};
135 }
136
137 =item $guard = $sem->guard
138
139 This method calls C<down> and then creates a guard object. When the guard
140 object is destroyed it automatically calls C<up>.
141
142 =item $guard = $sem->timed_guard($timeout)
143
144 Like C<guard>, but returns undef if semaphore couldn't be acquired within
145 $timeout seconds, otherwise the guard object.
146
147 =cut
148
149 sub guard {
150 &down;
151 # double indirection because bless works on the referenced
152 # object, not (only) on the reference itself.
153 bless \\$_[0], Coro::Semaphore::guard::;
154 }
155
156 sub timed_guard {
157 &timed_down
158 ? bless \\$_[0], Coro::Semaphore::guard::
159 : ();
160 }
161
162 sub Coro::Semaphore::guard::DESTROY {
163 &up(${${$_[0]}});
164 }
165
166 1;
167
168 =back
169
170 =head1 AUTHOR
171
172 Marc Lehmann <pcg@goof.com>
173 http://home.schmorp.de/
174
175 =cut
176