File Coverage

blib/lib/Thread/Semaphore.pm
Criterion Covered Total %
statement 46 52 88.4
branch 15 22 68.1
condition 5 6 83.3
subroutine 10 11 90.9
pod 6 6 100.0
total 82 97 84.5


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