1b39c5158Smillertpackage Thread::Semaphore;
2b39c5158Smillert
3b39c5158Smillertuse strict;
4b39c5158Smillertuse warnings;
5b39c5158Smillert
6*5759b3d2Safresh1our $VERSION = '2.13';
7898184e3Ssthen$VERSION = eval $VERSION;
8b39c5158Smillert
9b39c5158Smillertuse threads::shared;
10b39c5158Smillertuse Scalar::Util 1.10 qw(looks_like_number);
11b39c5158Smillert
12898184e3Ssthen# Predeclarations for internal functions
13898184e3Ssthenmy ($validate_arg);
14898184e3Ssthen
15b39c5158Smillert# Create a new semaphore optionally with specified count (count defaults to 1)
16b39c5158Smillertsub new {
17b39c5158Smillert    my $class = shift;
18898184e3Ssthen
19898184e3Ssthen    my $val :shared = 1;
20898184e3Ssthen    if (@_) {
21898184e3Ssthen        $val = shift;
22b39c5158Smillert        if (! defined($val) ||
23b39c5158Smillert            ! looks_like_number($val) ||
24b39c5158Smillert            (int($val) != $val))
25b39c5158Smillert        {
26b39c5158Smillert            require Carp;
27b39c5158Smillert            $val = 'undef' if (! defined($val));
28b39c5158Smillert            Carp::croak("Semaphore initializer is not an integer: $val");
29b39c5158Smillert        }
30898184e3Ssthen    }
31898184e3Ssthen
32b39c5158Smillert    return bless(\$val, $class);
33b39c5158Smillert}
34b39c5158Smillert
35b39c5158Smillert# Decrement a semaphore's count (decrement amount defaults to 1)
36b39c5158Smillertsub down {
37b39c5158Smillert    my $sema = shift;
38898184e3Ssthen    my $dec = @_ ? $validate_arg->(shift) : 1;
39898184e3Ssthen
40b39c5158Smillert    lock($$sema);
41b39c5158Smillert    cond_wait($$sema) until ($$sema >= $dec);
42b39c5158Smillert    $$sema -= $dec;
43b39c5158Smillert}
44b39c5158Smillert
45898184e3Ssthen# Decrement a semaphore's count only if count >= decrement value
46898184e3Ssthen#  (decrement amount defaults to 1)
47898184e3Ssthensub down_nb {
48898184e3Ssthen    my $sema = shift;
49898184e3Ssthen    my $dec = @_ ? $validate_arg->(shift) : 1;
50898184e3Ssthen
51898184e3Ssthen    lock($$sema);
52898184e3Ssthen    my $ok = ($$sema >= $dec);
53898184e3Ssthen    $$sema -= $dec if $ok;
54898184e3Ssthen    return $ok;
55898184e3Ssthen}
56898184e3Ssthen
57898184e3Ssthen# Decrement a semaphore's count even if the count goes below 0
58898184e3Ssthen#  (decrement amount defaults to 1)
59898184e3Ssthensub down_force {
60898184e3Ssthen    my $sema = shift;
61898184e3Ssthen    my $dec = @_ ? $validate_arg->(shift) : 1;
62898184e3Ssthen
63898184e3Ssthen    lock($$sema);
64898184e3Ssthen    $$sema -= $dec;
65898184e3Ssthen}
66898184e3Ssthen
67*5759b3d2Safresh1# Decrement a semaphore's count with timeout
68*5759b3d2Safresh1#  (timeout in seconds; decrement amount defaults to 1)
69*5759b3d2Safresh1sub down_timed {
70*5759b3d2Safresh1    my $sema = shift;
71*5759b3d2Safresh1    my $timeout = $validate_arg->(shift);
72*5759b3d2Safresh1    my $dec = @_ ? $validate_arg->(shift) : 1;
73*5759b3d2Safresh1
74*5759b3d2Safresh1    lock($$sema);
75*5759b3d2Safresh1    my $abs = time() + $timeout;
76*5759b3d2Safresh1    until ($$sema >= $dec) {
77*5759b3d2Safresh1        return if !cond_timedwait($$sema, $abs);
78*5759b3d2Safresh1    }
79*5759b3d2Safresh1    $$sema -= $dec;
80*5759b3d2Safresh1    return 1;
81*5759b3d2Safresh1}
82*5759b3d2Safresh1
83b39c5158Smillert# Increment a semaphore's count (increment amount defaults to 1)
84b39c5158Smillertsub up {
85b39c5158Smillert    my $sema = shift;
86898184e3Ssthen    my $inc = @_ ? $validate_arg->(shift) : 1;
87898184e3Ssthen
88b39c5158Smillert    lock($$sema);
89b39c5158Smillert    ($$sema += $inc) > 0 and cond_broadcast($$sema);
90b39c5158Smillert}
91b39c5158Smillert
92898184e3Ssthen### Internal Functions ###
93898184e3Ssthen
94898184e3Ssthen# Validate method argument
95898184e3Ssthen$validate_arg = sub {
96898184e3Ssthen    my $arg = shift;
97898184e3Ssthen
98898184e3Ssthen    if (! defined($arg) ||
99898184e3Ssthen        ! looks_like_number($arg) ||
100898184e3Ssthen        (int($arg) != $arg) ||
101898184e3Ssthen        ($arg < 1))
102898184e3Ssthen    {
103898184e3Ssthen        require Carp;
104898184e3Ssthen        my ($method) = (caller(1))[3];
105898184e3Ssthen        $method =~ s/Thread::Semaphore:://;
106898184e3Ssthen        $arg = 'undef' if (! defined($arg));
107898184e3Ssthen        Carp::croak("Argument to semaphore method '$method' is not a positive integer: $arg");
108898184e3Ssthen    }
109898184e3Ssthen
110898184e3Ssthen    return $arg;
111898184e3Ssthen};
112898184e3Ssthen
113b39c5158Smillert1;
114b39c5158Smillert
115b39c5158Smillert=head1 NAME
116b39c5158Smillert
117b39c5158SmillertThread::Semaphore - Thread-safe semaphores
118b39c5158Smillert
119b39c5158Smillert=head1 VERSION
120b39c5158Smillert
121*5759b3d2Safresh1This document describes Thread::Semaphore version 2.13
122b39c5158Smillert
123b39c5158Smillert=head1 SYNOPSIS
124b39c5158Smillert
125b39c5158Smillert    use Thread::Semaphore;
126b39c5158Smillert    my $s = Thread::Semaphore->new();
127b39c5158Smillert    $s->down();   # Also known as the semaphore P operation.
128b39c5158Smillert    # The guarded section is here
129b39c5158Smillert    $s->up();     # Also known as the semaphore V operation.
130b39c5158Smillert
131898184e3Ssthen    # Decrement the semaphore only if it would immediately succeed.
132898184e3Ssthen    if ($s->down_nb()) {
133898184e3Ssthen        # The guarded section is here
134898184e3Ssthen        $s->up();
135898184e3Ssthen    }
136898184e3Ssthen
137898184e3Ssthen    # Forcefully decrement the semaphore even if its count goes below 0.
138898184e3Ssthen    $s->down_force();
139898184e3Ssthen
140898184e3Ssthen    # The default value for semaphore operations is 1
141898184e3Ssthen    my $s = Thread::Semaphore->new($initial_value);
142b39c5158Smillert    $s->down($down_value);
143b39c5158Smillert    $s->up($up_value);
144898184e3Ssthen    if ($s->down_nb($down_value)) {
145898184e3Ssthen        ...
146898184e3Ssthen        $s->up($up_value);
147898184e3Ssthen    }
148898184e3Ssthen    $s->down_force($down_value);
149b39c5158Smillert
150b39c5158Smillert=head1 DESCRIPTION
151b39c5158Smillert
152b39c5158SmillertSemaphores provide a mechanism to regulate access to resources.  Unlike
153b39c5158Smillertlocks, semaphores aren't tied to particular scalars, and so may be used to
154b39c5158Smillertcontrol access to anything you care to use them for.
155b39c5158Smillert
156b39c5158SmillertSemaphores don't limit their values to zero and one, so they can be used to
157b39c5158Smillertcontrol access to some resource that there may be more than one of (e.g.,
158b39c5158Smillertfilehandles).  Increment and decrement amounts aren't fixed at one either,
159b39c5158Smillertso threads can reserve or return multiple resources at once.
160b39c5158Smillert
161b39c5158Smillert=head1 METHODS
162b39c5158Smillert
163b39c5158Smillert=over 8
164b39c5158Smillert
165b39c5158Smillert=item ->new()
166b39c5158Smillert
167b39c5158Smillert=item ->new(NUMBER)
168b39c5158Smillert
169b39c5158SmillertC<new> creates a new semaphore, and initializes its count to the specified
170b39c5158Smillertnumber (which must be an integer).  If no number is specified, the
171b39c5158Smillertsemaphore's count defaults to 1.
172b39c5158Smillert
173b39c5158Smillert=item ->down()
174b39c5158Smillert
175b39c5158Smillert=item ->down(NUMBER)
176b39c5158Smillert
177b39c5158SmillertThe C<down> method decreases the semaphore's count by the specified number
178b39c5158Smillert(which must be an integer >= 1), or by one if no number is specified.
179b39c5158Smillert
180b39c5158SmillertIf the semaphore's count would drop below zero, this method will block
181b39c5158Smillertuntil such time as the semaphore's count is greater than or equal to the
182b39c5158Smillertamount you're C<down>ing the semaphore's count by.
183b39c5158Smillert
184b39c5158SmillertThis is the semaphore "P operation" (the name derives from the Dutch
185b39c5158Smillertword "pak", which means "capture" -- the semaphore operations were
186b39c5158Smillertnamed by the late Dijkstra, who was Dutch).
187b39c5158Smillert
188898184e3Ssthen=item ->down_nb()
189898184e3Ssthen
190898184e3Ssthen=item ->down_nb(NUMBER)
191898184e3Ssthen
192898184e3SsthenThe C<down_nb> method attempts to decrease the semaphore's count by the
193898184e3Ssthenspecified number (which must be an integer >= 1), or by one if no number
194898184e3Ssthenis specified.
195898184e3Ssthen
196898184e3SsthenIf the semaphore's count would drop below zero, this method will return
197898184e3SsthenI<false>, and the semaphore's count remains unchanged.  Otherwise, the
198898184e3Ssthensemaphore's count is decremented and this method returns I<true>.
199898184e3Ssthen
200898184e3Ssthen=item ->down_force()
201898184e3Ssthen
202898184e3Ssthen=item ->down_force(NUMBER)
203898184e3Ssthen
204898184e3SsthenThe C<down_force> method decreases the semaphore's count by the specified
205898184e3Ssthennumber (which must be an integer >= 1), or by one if no number is specified.
206898184e3SsthenThis method does not block, and may cause the semaphore's count to drop
207898184e3Ssthenbelow zero.
208898184e3Ssthen
209*5759b3d2Safresh1=item ->down_timed(TIMEOUT)
210*5759b3d2Safresh1
211*5759b3d2Safresh1=item ->down_timed(TIMEOUT, NUMBER)
212*5759b3d2Safresh1
213*5759b3d2Safresh1The C<down_timed> method attempts to decrease the semaphore's count by 1
214*5759b3d2Safresh1or by the specified number within the specified timeout period given in
215*5759b3d2Safresh1seconds (which must be an integer >= 0).
216*5759b3d2Safresh1
217*5759b3d2Safresh1If the semaphore's count would drop below zero, this method will block
218*5759b3d2Safresh1until either the semaphore's count is greater than or equal to the
219*5759b3d2Safresh1amount you're C<down>ing the semaphore's count by, or until the timeout is
220*5759b3d2Safresh1reached.
221*5759b3d2Safresh1
222*5759b3d2Safresh1If the timeout is reached, this method will return I<false>, and the
223*5759b3d2Safresh1semaphore's count remains unchanged.  Otherwise, the semaphore's count is
224*5759b3d2Safresh1decremented and this method returns I<true>.
225*5759b3d2Safresh1
226b39c5158Smillert=item ->up()
227b39c5158Smillert
228b39c5158Smillert=item ->up(NUMBER)
229b39c5158Smillert
230b39c5158SmillertThe C<up> method increases the semaphore's count by the number specified
231b39c5158Smillert(which must be an integer >= 1), or by one if no number is specified.
232b39c5158Smillert
233b39c5158SmillertThis will unblock any thread that is blocked trying to C<down> the
234b39c5158Smillertsemaphore if the C<up> raises the semaphore's count above the amount that
235b39c5158Smillertthe C<down> is trying to decrement it by.  For example, if three threads
236b39c5158Smillertare blocked trying to C<down> a semaphore by one, and another thread C<up>s
237b39c5158Smillertthe semaphore by two, then two of the blocked threads (which two is
238b39c5158Smillertindeterminate) will become unblocked.
239b39c5158Smillert
240b39c5158SmillertThis is the semaphore "V operation" (the name derives from the Dutch
241b39c5158Smillertword "vrij", which means "release").
242b39c5158Smillert
243b39c5158Smillert=back
244b39c5158Smillert
245b39c5158Smillert=head1 NOTES
246b39c5158Smillert
247b39c5158SmillertSemaphores created by L<Thread::Semaphore> can be used in both threaded and
248b39c5158Smillertnon-threaded applications.  This allows you to write modules and packages
249b39c5158Smillertthat potentially make use of semaphores, and that will function in either
250b39c5158Smillertenvironment.
251b39c5158Smillert
252b39c5158Smillert=head1 SEE ALSO
253b39c5158Smillert
254*5759b3d2Safresh1Thread::Semaphore on MetaCPAN:
255*5759b3d2Safresh1L<https://metacpan.org/release/Thread-Semaphore>
256*5759b3d2Safresh1
257*5759b3d2Safresh1Code repository for CPAN distribution:
258*5759b3d2Safresh1L<https://github.com/Dual-Life/Thread-Semaphore>
259b39c5158Smillert
260b39c5158SmillertL<threads>, L<threads::shared>
261b39c5158Smillert
262*5759b3d2Safresh1Sample code in the I<examples> directory of this distribution on CPAN.
263*5759b3d2Safresh1
264b39c5158Smillert=head1 MAINTAINER
265b39c5158Smillert
266b39c5158SmillertJerry D. Hedden, S<E<lt>jdhedden AT cpan DOT orgE<gt>>
267b39c5158Smillert
268b39c5158Smillert=head1 LICENSE
269b39c5158Smillert
270b39c5158SmillertThis program is free software; you can redistribute it and/or modify it under
271b39c5158Smillertthe same terms as Perl itself.
272b39c5158Smillert
273b39c5158Smillert=cut
274