1package Thread::Semaphore; 2 3use strict; 4use warnings; 5 6our $VERSION = '2.13'; 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# Decrement a semaphore's count with timeout 68# (timeout in seconds; decrement amount defaults to 1) 69sub down_timed { 70 my $sema = shift; 71 my $timeout = $validate_arg->(shift); 72 my $dec = @_ ? $validate_arg->(shift) : 1; 73 74 lock($$sema); 75 my $abs = time() + $timeout; 76 until ($$sema >= $dec) { 77 return if !cond_timedwait($$sema, $abs); 78 } 79 $$sema -= $dec; 80 return 1; 81} 82 83# Increment a semaphore's count (increment amount defaults to 1) 84sub up { 85 my $sema = shift; 86 my $inc = @_ ? $validate_arg->(shift) : 1; 87 88 lock($$sema); 89 ($$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 1131; 114 115=head1 NAME 116 117Thread::Semaphore - Thread-safe semaphores 118 119=head1 VERSION 120 121This 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 152Semaphores provide a mechanism to regulate access to resources. Unlike 153locks, semaphores aren't tied to particular scalars, and so may be used to 154control access to anything you care to use them for. 155 156Semaphores don't limit their values to zero and one, so they can be used to 157control access to some resource that there may be more than one of (e.g., 158filehandles). Increment and decrement amounts aren't fixed at one either, 159so 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 169C<new> creates a new semaphore, and initializes its count to the specified 170number (which must be an integer). If no number is specified, the 171semaphore's count defaults to 1. 172 173=item ->down() 174 175=item ->down(NUMBER) 176 177The C<down> 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 180If the semaphore's count would drop below zero, this method will block 181until such time as the semaphore's count is greater than or equal to the 182amount you're C<down>ing the semaphore's count by. 183 184This is the semaphore "P operation" (the name derives from the Dutch 185word "pak", which means "capture" -- the semaphore operations were 186named by the late Dijkstra, who was Dutch). 187 188=item ->down_nb() 189 190=item ->down_nb(NUMBER) 191 192The C<down_nb> method attempts to decrease the semaphore's count by the 193specified number (which must be an integer >= 1), or by one if no number 194is specified. 195 196If the semaphore's count would drop below zero, this method will return 197I<false>, and the semaphore's count remains unchanged. Otherwise, the 198semaphore's count is decremented and this method returns I<true>. 199 200=item ->down_force() 201 202=item ->down_force(NUMBER) 203 204The C<down_force> method decreases the semaphore's count by the specified 205number (which must be an integer >= 1), or by one if no number is specified. 206This method does not block, and may cause the semaphore's count to drop 207below zero. 208 209=item ->down_timed(TIMEOUT) 210 211=item ->down_timed(TIMEOUT, NUMBER) 212 213The C<down_timed> method attempts to decrease the semaphore's count by 1 214or by the specified number within the specified timeout period given in 215seconds (which must be an integer >= 0). 216 217If the semaphore's count would drop below zero, this method will block 218until either the semaphore's count is greater than or equal to the 219amount you're C<down>ing the semaphore's count by, or until the timeout is 220reached. 221 222If the timeout is reached, this method will return I<false>, and the 223semaphore's count remains unchanged. Otherwise, the semaphore's count is 224decremented and this method returns I<true>. 225 226=item ->up() 227 228=item ->up(NUMBER) 229 230The C<up> 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 233This will unblock any thread that is blocked trying to C<down> the 234semaphore if the C<up> raises the semaphore's count above the amount that 235the C<down> is trying to decrement it by. For example, if three threads 236are blocked trying to C<down> a semaphore by one, and another thread C<up>s 237the semaphore by two, then two of the blocked threads (which two is 238indeterminate) will become unblocked. 239 240This is the semaphore "V operation" (the name derives from the Dutch 241word "vrij", which means "release"). 242 243=back 244 245=head1 NOTES 246 247Semaphores created by L<Thread::Semaphore> can be used in both threaded and 248non-threaded applications. This allows you to write modules and packages 249that potentially make use of semaphores, and that will function in either 250environment. 251 252=head1 SEE ALSO 253 254Thread::Semaphore on MetaCPAN: 255L<https://metacpan.org/release/Thread-Semaphore> 256 257Code repository for CPAN distribution: 258L<https://github.com/Dual-Life/Thread-Semaphore> 259 260L<threads>, L<threads::shared> 261 262Sample code in the I<examples> directory of this distribution on CPAN. 263 264=head1 MAINTAINER 265 266Jerry D. Hedden, S<E<lt>jdhedden AT cpan DOT orgE<gt>> 267 268=head1 LICENSE 269 270This program is free software; you can redistribute it and/or modify it under 271the same terms as Perl itself. 272 273=cut 274