[PATCH] Upgrade to Thread-Semaphore 2.11

[email protected] ("Jerry D. Hedden")
Newsgroups perl.perl5.porters
Message-ID <[email protected]>
Added new method ->down_nb() at the suggestion of Rick Garlick.

Refactored methods to skip argument validation when no argument is supplied.
0001-Upgrade-to-Thread-Semaphore-2.11.patch (application/octet-stream, 10.1 KB)
From ab4a4850e2634173100a0583f26413b3f210eb5b Mon Sep 17 00:00:00 2001
From: Jerry D. Hedden <[email protected]>
Date: Thu, 10 Jun 2010 14:22:56 -0400
Subject: [PATCH] Upgrade to Thread::Semaphore 2.11

---
 MANIFEST                                      |    1 +
 Porting/Maintainers.pl                        |    2 +-
 dist/Thread-Semaphore/lib/Thread/Semaphore.pm |  113 ++++++++++++++++++-------
 dist/Thread-Semaphore/t/02_errs.t             |   21 ++++-
 dist/Thread-Semaphore/t/03_nothreads.t        |    4 +-
 dist/Thread-Semaphore/t/04_nonblocking.t      |   62 ++++++++++++++
 6 files changed, 168 insertions(+), 35 deletions(-)
 create mode 100644 dist/Thread-Semaphore/t/04_nonblocking.t

diff --git a/MANIFEST b/MANIFEST
index 197d359..ddef82d 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -2831,6 +2831,7 @@ dist/Thread-Semaphore/lib/Thread/Semaphore.pm	Thread-safe semaphores
 dist/Thread-Semaphore/t/01_basic.t		Thread::Semaphore tests
 dist/Thread-Semaphore/t/02_errs.t		Thread::Semaphore tests
 dist/Thread-Semaphore/t/03_nothreads.t		Thread::Semaphore tests
+dist/Thread-Semaphore/t/04_nonblocking.t	Thread::Semaphore tests
 dist/threads/hints/hpux.pl	Hint file for HPUX
 dist/threads/hints/linux.pl	Hint file for Linux
 dist/threads/Makefile.PL	ithreads
diff --git a/Porting/Maintainers.pl b/Porting/Maintainers.pl
index 49e6525..2c39cf6 100755
--- a/Porting/Maintainers.pl
+++ b/Porting/Maintainers.pl
@@ -1406,7 +1406,7 @@ use File::Glob qw(:case);
     'Thread::Semaphore' =>
 	{
 	'MAINTAINER'	=> 'jdhedden',
-	'DISTRIBUTION'	=> 'JDHEDDEN/Thread-Semaphore-2.09.tar.gz',
+	'DISTRIBUTION'	=> 'JDHEDDEN/Thread-Semaphore-2.11.tar.gz',
 	'FILES'		=> q[dist/Thread-Semaphore],
 	'EXCLUDED'	=> [ qw(examples/semaphore.pl
 				t/00_load.t
diff --git a/dist/Thread-Semaphore/lib/Thread/Semaphore.pm b/dist/Thread-Semaphore/lib/Thread/Semaphore.pm
index 67cb30e..dbebe9a 100644
--- a/dist/Thread-Semaphore/lib/Thread/Semaphore.pm
+++ b/dist/Thread-Semaphore/lib/Thread/Semaphore.pm
@@ -3,23 +3,32 @@ package Thread::Semaphore;
 use strict;
 use warnings;
 
-our $VERSION = '2.09';
+our $VERSION = '2.11';
+$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 = @_ ? shift : 1;
-    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");
+
+    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);
 }
 
@@ -27,36 +36,56 @@ sub new {
 sub down {
     my $sema = shift;
     lock($$sema);
-    my $dec = @_ ? shift : 1;
-    if (! defined($dec) ||
-        ! looks_like_number($dec) ||
-        (int($dec) != $dec) ||
-        ($dec < 1))
-    {
-        require Carp;
-        $dec = 'undef' if (! defined($dec));
-        Carp::croak("Semaphore decrement is not a positive integer: $dec");
-    }
+
+    my $dec = @_ ? $validate_arg->(shift) : 1;
+
     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;
+    lock($$sema);
+
+    my $dec = @_ ? $validate_arg->(shift) : 1;
+
+    my $ok = ($$sema >= $dec);
+    $$sema -= $dec if $ok;
+    return $ok;
+}
+
 # Increment a semaphore's count (increment amount defaults to 1)
 sub up {
     my $sema = shift;
     lock($$sema);
-    my $inc = @_ ? shift : 1;
-    if (! defined($inc) ||
-        ! looks_like_number($inc) ||
-        (int($inc) != $inc) ||
-        ($inc < 1))
+
+    my $inc = @_ ? $validate_arg->(shift) : 1;
+
+    ($$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;
-        $inc = 'undef' if (! defined($inc));
-        Carp::croak("Semaphore increment is not a positive integer: $inc");
+        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");
     }
-    ($$sema += $inc) > 0 and cond_broadcast($$sema);
-}
+
+    return $arg;
+};
 
 1;
 
@@ -66,7 +95,7 @@ Thread::Semaphore - Thread-safe semaphores
 
 =head1 VERSION
 
-This document describes Thread::Semaphore version 2.09
+This document describes Thread::Semaphore version 2.11
 
 =head1 SYNOPSIS
 
@@ -76,10 +105,20 @@ This document describes Thread::Semaphore version 2.09
     # The guarded section is here
     $s->up();     # Also known as the semaphore V operation.
 
-    # The default semaphore value is 1
+    # Decrement the semaphore only if it would immediately succeed.
+    if ($s->down_nb()) {
+        # The guarded section is here
+        $s->up();
+    }
+
+    # 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);
+    }
 
 =head1 DESCRIPTION
 
@@ -119,6 +158,18 @@ 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 ->up()
 
 =item ->up(NUMBER)
@@ -151,7 +202,7 @@ Thread::Semaphore Discussion Forum on CPAN:
 L<http://www.cpanforum.com/dist/Thread-Semaphore>
 
 Annotated POD for Thread::Semaphore:
-L<http://annocpan.org/~JDHEDDEN/Thread-Semaphore-2.09/lib/Thread/Semaphore.pm>
+L<http://annocpan.org/~JDHEDDEN/Thread-Semaphore-2.11/lib/Thread/Semaphore.pm>
 
 Source repository:
 L<http://code.google.com/p/thread-semaphore/>
diff --git a/dist/Thread-Semaphore/t/02_errs.t b/dist/Thread-Semaphore/t/02_errs.t
index 45b0aa9..879b2ad 100644
--- a/dist/Thread-Semaphore/t/02_errs.t
+++ b/dist/Thread-Semaphore/t/02_errs.t
@@ -3,9 +3,9 @@ use warnings;
 
 use Thread::Semaphore;
 
-use Test::More 'tests' => 12;
+use Test::More 'tests' => 19;
 
-my $err = qr/^Semaphore .* is not .* integer: /;
+my $err = qr/^Semaphore initializer is not an integer: /;
 
 eval { Thread::Semaphore->new(undef); };
 like($@, $err, $@);
@@ -17,8 +17,12 @@ like($@, $err, $@);
 my $s = Thread::Semaphore->new();
 ok($s, 'New semaphore');
 
+$err = qr/^Argument to semaphore method .* is not a positive integer: /;
+
 eval { $s->down(undef); };
 like($@, $err, $@);
+eval { $s->down(0); };
+like($@, $err, $@);
 eval { $s->down(-1); };
 like($@, $err, $@);
 eval { $s->down(1.5); };
@@ -26,8 +30,21 @@ like($@, $err, $@);
 eval { $s->down('foo'); };
 like($@, $err, $@);
 
+eval { $s->down_nb(undef); };
+like($@, $err, $@);
+eval { $s->down_nb(0); };
+like($@, $err, $@);
+eval { $s->down_nb(-1); };
+like($@, $err, $@);
+eval { $s->down_nb(1.5); };
+like($@, $err, $@);
+eval { $s->down_nb('foo'); };
+like($@, $err, $@);
+
 eval { $s->up(undef); };
 like($@, $err, $@);
+eval { $s->up(0); };
+like($@, $err, $@);
 eval { $s->up(-1); };
 like($@, $err, $@);
 eval { $s->up(1.5); };
diff --git a/dist/Thread-Semaphore/t/03_nothreads.t b/dist/Thread-Semaphore/t/03_nothreads.t
index f0454be..b8b2f0f 100644
--- a/dist/Thread-Semaphore/t/03_nothreads.t
+++ b/dist/Thread-Semaphore/t/03_nothreads.t
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 
-use Test::More 'tests' => 4;
+use Test::More 'tests' => 6;
 
 use Thread::Semaphore;
 
@@ -13,6 +13,8 @@ $s->up(2);
 is($$s, 2, 'Non-threaded semaphore');
 $s->down();
 is($$s, 1, 'Non-threaded semaphore');
+ok(! $s->down_nb(2), 'Non-threaded semaphore');
+ok($s->down_nb(), 'Non-threaded semaphore');
 
 exit(0);
 
diff --git a/dist/Thread-Semaphore/t/04_nonblocking.t b/dist/Thread-Semaphore/t/04_nonblocking.t
new file mode 100644
index 0000000..9c06969
--- /dev/null
+++ b/dist/Thread-Semaphore/t/04_nonblocking.t
@@ -0,0 +1,62 @@
+use strict;
+use warnings;
+
+BEGIN {
+    use Config;
+    if (! $Config{'useithreads'}) {
+        print("1..0 # SKIP Perl not compiled with 'useithreads'\n");
+        exit(0);
+    }
+}
+
+use threads;
+use threads::shared;
+use Thread::Semaphore;
+
+if ($] == 5.008) {
+    require 't/test.pl';   # Test::More work-alike for Perl 5.8.0
+} else {
+    require Test::More;
+}
+Test::More->import();
+plan('tests' => 13);
+
+### Basic usage with multiple threads ###
+
+my $sm = Thread::Semaphore->new(0);
+my $st = Thread::Semaphore->new(0);
+ok($sm, 'New Semaphore');
+ok($st, 'New Semaphore');
+
+my $token :shared = 0;
+
+threads->create(sub {
+    ok(! $st->down_nb(), 'Semaphore unavailable to thread');
+    $sm->up();
+
+    $st->down(2);
+    ok(! $st->down_nb(5), 'Semaphore unavailable to thread');
+    ok($st->down_nb(2), 'Thread 1 got semaphore');
+    ok(! $st->down_nb(2), 'Semaphore unavailable to thread');
+    ok($st->down_nb(1), 'Thread 1 got semaphore');
+    ok(! $st->down_nb(), 'Semaphore unavailable to thread');
+    is($token++, 1, 'Thread done');
+    $sm->up();
+})->detach();
+
+$sm->down(1);
+is($token++, 0, 'Main has semaphore');
+$st->up();
+
+ok(! $sm->down_nb(), 'Semaphore unavailable to main');
+$st->up(4);
+
+$sm->down();
+is($token++, 2, 'Main got semaphore');
+
+ok(1, 'Main done');
+threads::yield();
+
+exit(0);
+
+# EOF
-- 
1.6.1.2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.