xref: /openbsd-src/gnu/usr.bin/perl/dist/Thread-Semaphore/lib/Thread/Semaphore.pm (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1package Thread::Semaphore;
2
3use strict;
4use warnings;
5
6our $VERSION = '2.12';
7$VERSION = eval $VERSION;
8
9use threads::shared;
10use Scalar::Util 1.10 qw(looks_like_number);
11
12# Predeclarations for internal functions
13my ($validate_arg);
14
15# Create a new semaphore optionally with specified count (count defaults to 1)
16sub new {
17    my $class = shift;
18
19    my $val :shared = 1;
20    if (@_) {
21        $val = shift;
22        if (! defined($val) ||
23            ! looks_like_number($val) ||
24            (int($val) != $val))
25        {
26            require Carp;
27            $val = 'undef' if (! defined($val));
28            Carp::croak("Semaphore initializer is not an integer: $val");
29        }
30    }
31
32    return bless(\$val, $class);
33}
34
35# Decrement a semaphore's count (decrement amount defaults to 1)
36sub down {
37    my $sema = shift;
38    my $dec = @_ ? $validate_arg->(shift) : 1;
39
40    lock($$sema);
41    cond_wait($$sema) until ($$sema >= $dec);
42    $$sema -= $dec;
43}
44
45# Decrement a semaphore's count only if count >= decrement value
46#  (decrement amount defaults to 1)
47sub down_nb {
48    my $sema = shift;
49    my $dec = @_ ? $validate_arg->(shift) : 1;
50
51    lock($$sema);
52    my $ok = ($$sema >= $dec);
53    $$sema -= $dec if $ok;
54    return $ok;
55}
56
57# Decrement a semaphore's count even if the count goes below 0
58#  (decrement amount defaults to 1)
59sub down_force {
60    my $sema = shift;
61    my $dec = @_ ? $validate_arg->(shift) : 1;
62
63    lock($$sema);
64    $$sema -= $dec;
65}
66
67# Increment a semaphore's count (increment amount defaults to 1)
68sub up {
69    my $sema = shift;
70    my $inc = @_ ? $validate_arg->(shift) : 1;
71
72    lock($$sema);
73    ($$sema += $inc) > 0 and cond_broadcast($$sema);
74}
75
76### Internal Functions ###
77
78# Validate method argument
79$validate_arg = sub {
80    my $arg = shift;
81
82    if (! defined($arg) ||
83        ! looks_like_number($arg) ||
84        (int($arg) != $arg) ||
85        ($arg < 1))
86    {
87        require Carp;
88        my ($method) = (caller(1))[3];
89        $method =~ s/Thread::Semaphore:://;
90        $arg = 'undef' if (! defined($arg));
91        Carp::croak("Argument to semaphore method '$method' is not a positive integer: $arg");
92    }
93
94    return $arg;
95};
96
971;
98
99=head1 NAME
100
101Thread::Semaphore - Thread-safe semaphores
102
103=head1 VERSION
104
105This document describes Thread::Semaphore version 2.12
106
107=head1 SYNOPSIS
108
109    use Thread::Semaphore;
110    my $s = Thread::Semaphore->new();
111    $s->down();   # Also known as the semaphore P operation.
112    # The guarded section is here
113    $s->up();     # Also known as the semaphore V operation.
114
115    # Decrement the semaphore only if it would immediately succeed.
116    if ($s->down_nb()) {
117        # The guarded section is here
118        $s->up();
119    }
120
121    # Forcefully decrement the semaphore even if its count goes below 0.
122    $s->down_force();
123
124    # The default value for semaphore operations is 1
125    my $s = Thread::Semaphore->new($initial_value);
126    $s->down($down_value);
127    $s->up($up_value);
128    if ($s->down_nb($down_value)) {
129        ...
130        $s->up($up_value);
131    }
132    $s->down_force($down_value);
133
134=head1 DESCRIPTION
135
136Semaphores provide a mechanism to regulate access to resources.  Unlike
137locks, semaphores aren't tied to particular scalars, and so may be used to
138control access to anything you care to use them for.
139
140Semaphores don't limit their values to zero and one, so they can be used to
141control access to some resource that there may be more than one of (e.g.,
142filehandles).  Increment and decrement amounts aren't fixed at one either,
143so threads can reserve or return multiple resources at once.
144
145=head1 METHODS
146
147=over 8
148
149=item ->new()
150
151=item ->new(NUMBER)
152
153C<new> creates a new semaphore, and initializes its count to the specified
154number (which must be an integer).  If no number is specified, the
155semaphore's count defaults to 1.
156
157=item ->down()
158
159=item ->down(NUMBER)
160
161The C<down> method decreases the semaphore's count by the specified number
162(which must be an integer >= 1), or by one if no number is specified.
163
164If the semaphore's count would drop below zero, this method will block
165until such time as the semaphore's count is greater than or equal to the
166amount you're C<down>ing the semaphore's count by.
167
168This is the semaphore "P operation" (the name derives from the Dutch
169word "pak", which means "capture" -- the semaphore operations were
170named by the late Dijkstra, who was Dutch).
171
172=item ->down_nb()
173
174=item ->down_nb(NUMBER)
175
176The C<down_nb> method attempts to decrease the semaphore's count by the
177specified number (which must be an integer >= 1), or by one if no number
178is specified.
179
180If the semaphore's count would drop below zero, this method will return
181I<false>, and the semaphore's count remains unchanged.  Otherwise, the
182semaphore's count is decremented and this method returns I<true>.
183
184=item ->down_force()
185
186=item ->down_force(NUMBER)
187
188The C<down_force> method decreases the semaphore's count by the specified
189number (which must be an integer >= 1), or by one if no number is specified.
190This method does not block, and may cause the semaphore's count to drop
191below zero.
192
193=item ->up()
194
195=item ->up(NUMBER)
196
197The C<up> method increases the semaphore's count by the number specified
198(which must be an integer >= 1), or by one if no number is specified.
199
200This will unblock any thread that is blocked trying to C<down> the
201semaphore if the C<up> raises the semaphore's count above the amount that
202the C<down> is trying to decrement it by.  For example, if three threads
203are blocked trying to C<down> a semaphore by one, and another thread C<up>s
204the semaphore by two, then two of the blocked threads (which two is
205indeterminate) will become unblocked.
206
207This is the semaphore "V operation" (the name derives from the Dutch
208word "vrij", which means "release").
209
210=back
211
212=head1 NOTES
213
214Semaphores created by L<Thread::Semaphore> can be used in both threaded and
215non-threaded applications.  This allows you to write modules and packages
216that potentially make use of semaphores, and that will function in either
217environment.
218
219=head1 SEE ALSO
220
221Thread::Semaphore Discussion Forum on CPAN:
222L<http://www.cpanforum.com/dist/Thread-Semaphore>
223
224L<threads>, L<threads::shared>
225
226=head1 MAINTAINER
227
228Jerry D. Hedden, S<E<lt>jdhedden AT cpan DOT orgE<gt>>
229
230=head1 LICENSE
231
232This program is free software; you can redistribute it and/or modify it under
233the same terms as Perl itself.
234
235=cut
236