Initial Commit
This commit is contained in:
273
database/perl/lib/Thread/Semaphore.pm
Normal file
273
database/perl/lib/Thread/Semaphore.pm
Normal file
@@ -0,0 +1,273 @@
|
||||
package Thread::Semaphore;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '2.13';
|
||||
$VERSION = eval $VERSION;
|
||||
|
||||
use threads::shared;
|
||||
use Scalar::Util 1.10 qw(looks_like_number);
|
||||
|
||||
# Predeclarations for internal functions
|
||||
my ($validate_arg);
|
||||
|
||||
# Create a new semaphore optionally with specified count (count defaults to 1)
|
||||
sub new {
|
||||
my $class = shift;
|
||||
|
||||
my $val :shared = 1;
|
||||
if (@_) {
|
||||
$val = shift;
|
||||
if (! defined($val) ||
|
||||
! looks_like_number($val) ||
|
||||
(int($val) != $val))
|
||||
{
|
||||
require Carp;
|
||||
$val = 'undef' if (! defined($val));
|
||||
Carp::croak("Semaphore initializer is not an integer: $val");
|
||||
}
|
||||
}
|
||||
|
||||
return bless(\$val, $class);
|
||||
}
|
||||
|
||||
# Decrement a semaphore's count (decrement amount defaults to 1)
|
||||
sub down {
|
||||
my $sema = shift;
|
||||
my $dec = @_ ? $validate_arg->(shift) : 1;
|
||||
|
||||
lock($$sema);
|
||||
cond_wait($$sema) until ($$sema >= $dec);
|
||||
$$sema -= $dec;
|
||||
}
|
||||
|
||||
# Decrement a semaphore's count only if count >= decrement value
|
||||
# (decrement amount defaults to 1)
|
||||
sub down_nb {
|
||||
my $sema = shift;
|
||||
my $dec = @_ ? $validate_arg->(shift) : 1;
|
||||
|
||||
lock($$sema);
|
||||
my $ok = ($$sema >= $dec);
|
||||
$$sema -= $dec if $ok;
|
||||
return $ok;
|
||||
}
|
||||
|
||||
# Decrement a semaphore's count even if the count goes below 0
|
||||
# (decrement amount defaults to 1)
|
||||
sub down_force {
|
||||
my $sema = shift;
|
||||
my $dec = @_ ? $validate_arg->(shift) : 1;
|
||||
|
||||
lock($$sema);
|
||||
$$sema -= $dec;
|
||||
}
|
||||
|
||||
# Decrement a semaphore's count with timeout
|
||||
# (timeout in seconds; decrement amount defaults to 1)
|
||||
sub down_timed {
|
||||
my $sema = shift;
|
||||
my $timeout = $validate_arg->(shift);
|
||||
my $dec = @_ ? $validate_arg->(shift) : 1;
|
||||
|
||||
lock($$sema);
|
||||
my $abs = time() + $timeout;
|
||||
until ($$sema >= $dec) {
|
||||
return if !cond_timedwait($$sema, $abs);
|
||||
}
|
||||
$$sema -= $dec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Increment a semaphore's count (increment amount defaults to 1)
|
||||
sub up {
|
||||
my $sema = shift;
|
||||
my $inc = @_ ? $validate_arg->(shift) : 1;
|
||||
|
||||
lock($$sema);
|
||||
($$sema += $inc) > 0 and cond_broadcast($$sema);
|
||||
}
|
||||
|
||||
### Internal Functions ###
|
||||
|
||||
# Validate method argument
|
||||
$validate_arg = sub {
|
||||
my $arg = shift;
|
||||
|
||||
if (! defined($arg) ||
|
||||
! looks_like_number($arg) ||
|
||||
(int($arg) != $arg) ||
|
||||
($arg < 1))
|
||||
{
|
||||
require Carp;
|
||||
my ($method) = (caller(1))[3];
|
||||
$method =~ s/Thread::Semaphore:://;
|
||||
$arg = 'undef' if (! defined($arg));
|
||||
Carp::croak("Argument to semaphore method '$method' is not a positive integer: $arg");
|
||||
}
|
||||
|
||||
return $arg;
|
||||
};
|
||||
|
||||
1;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Thread::Semaphore - Thread-safe semaphores
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
This document describes Thread::Semaphore version 2.13
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Thread::Semaphore;
|
||||
my $s = Thread::Semaphore->new();
|
||||
$s->down(); # Also known as the semaphore P operation.
|
||||
# The guarded section is here
|
||||
$s->up(); # Also known as the semaphore V operation.
|
||||
|
||||
# Decrement the semaphore only if it would immediately succeed.
|
||||
if ($s->down_nb()) {
|
||||
# The guarded section is here
|
||||
$s->up();
|
||||
}
|
||||
|
||||
# Forcefully decrement the semaphore even if its count goes below 0.
|
||||
$s->down_force();
|
||||
|
||||
# The default value for semaphore operations is 1
|
||||
my $s = Thread::Semaphore->new($initial_value);
|
||||
$s->down($down_value);
|
||||
$s->up($up_value);
|
||||
if ($s->down_nb($down_value)) {
|
||||
...
|
||||
$s->up($up_value);
|
||||
}
|
||||
$s->down_force($down_value);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Semaphores provide a mechanism to regulate access to resources. Unlike
|
||||
locks, semaphores aren't tied to particular scalars, and so may be used to
|
||||
control access to anything you care to use them for.
|
||||
|
||||
Semaphores don't limit their values to zero and one, so they can be used to
|
||||
control access to some resource that there may be more than one of (e.g.,
|
||||
filehandles). Increment and decrement amounts aren't fixed at one either,
|
||||
so threads can reserve or return multiple resources at once.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over 8
|
||||
|
||||
=item ->new()
|
||||
|
||||
=item ->new(NUMBER)
|
||||
|
||||
C<new> creates a new semaphore, and initializes its count to the specified
|
||||
number (which must be an integer). If no number is specified, the
|
||||
semaphore's count defaults to 1.
|
||||
|
||||
=item ->down()
|
||||
|
||||
=item ->down(NUMBER)
|
||||
|
||||
The C<down> method decreases the semaphore's count by the specified number
|
||||
(which must be an integer >= 1), or by one if no number is specified.
|
||||
|
||||
If the semaphore's count would drop below zero, this method will block
|
||||
until such time as the semaphore's count is greater than or equal to the
|
||||
amount you're C<down>ing the semaphore's count by.
|
||||
|
||||
This is the semaphore "P operation" (the name derives from the Dutch
|
||||
word "pak", which means "capture" -- the semaphore operations were
|
||||
named by the late Dijkstra, who was Dutch).
|
||||
|
||||
=item ->down_nb()
|
||||
|
||||
=item ->down_nb(NUMBER)
|
||||
|
||||
The C<down_nb> method attempts to decrease the semaphore's count by the
|
||||
specified number (which must be an integer >= 1), or by one if no number
|
||||
is specified.
|
||||
|
||||
If the semaphore's count would drop below zero, this method will return
|
||||
I<false>, and the semaphore's count remains unchanged. Otherwise, the
|
||||
semaphore's count is decremented and this method returns I<true>.
|
||||
|
||||
=item ->down_force()
|
||||
|
||||
=item ->down_force(NUMBER)
|
||||
|
||||
The C<down_force> method decreases the semaphore's count by the specified
|
||||
number (which must be an integer >= 1), or by one if no number is specified.
|
||||
This method does not block, and may cause the semaphore's count to drop
|
||||
below zero.
|
||||
|
||||
=item ->down_timed(TIMEOUT)
|
||||
|
||||
=item ->down_timed(TIMEOUT, NUMBER)
|
||||
|
||||
The C<down_timed> method attempts to decrease the semaphore's count by 1
|
||||
or by the specified number within the specified timeout period given in
|
||||
seconds (which must be an integer >= 0).
|
||||
|
||||
If the semaphore's count would drop below zero, this method will block
|
||||
until either the semaphore's count is greater than or equal to the
|
||||
amount you're C<down>ing the semaphore's count by, or until the timeout is
|
||||
reached.
|
||||
|
||||
If the timeout is reached, this method will return I<false>, and the
|
||||
semaphore's count remains unchanged. Otherwise, the semaphore's count is
|
||||
decremented and this method returns I<true>.
|
||||
|
||||
=item ->up()
|
||||
|
||||
=item ->up(NUMBER)
|
||||
|
||||
The C<up> method increases the semaphore's count by the number specified
|
||||
(which must be an integer >= 1), or by one if no number is specified.
|
||||
|
||||
This will unblock any thread that is blocked trying to C<down> the
|
||||
semaphore if the C<up> raises the semaphore's count above the amount that
|
||||
the C<down> is trying to decrement it by. For example, if three threads
|
||||
are blocked trying to C<down> a semaphore by one, and another thread C<up>s
|
||||
the semaphore by two, then two of the blocked threads (which two is
|
||||
indeterminate) will become unblocked.
|
||||
|
||||
This is the semaphore "V operation" (the name derives from the Dutch
|
||||
word "vrij", which means "release").
|
||||
|
||||
=back
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
Semaphores created by L<Thread::Semaphore> can be used in both threaded and
|
||||
non-threaded applications. This allows you to write modules and packages
|
||||
that potentially make use of semaphores, and that will function in either
|
||||
environment.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Thread::Semaphore on MetaCPAN:
|
||||
L<https://metacpan.org/release/Thread-Semaphore>
|
||||
|
||||
Code repository for CPAN distribution:
|
||||
L<https://github.com/Dual-Life/Thread-Semaphore>
|
||||
|
||||
L<threads>, L<threads::shared>
|
||||
|
||||
Sample code in the I<examples> directory of this distribution on CPAN.
|
||||
|
||||
=head1 MAINTAINER
|
||||
|
||||
Jerry D. Hedden, S<E<lt>jdhedden AT cpan DOT orgE<gt>>
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user