[libmath-prime-util-perl] 08/16: Thread safety

Partha P. Mukherjee ppm-guest at moszumanska.debian.org
Thu May 21 18:44:34 UTC 2015


This is an automated email from the git hooks/post-receive script.

ppm-guest pushed a commit to annotated tag v0.08
in repository libmath-prime-util-perl.

commit fc1e8c6b29e38f36d629b3174d8c17cb654d3106
Author: Dana Jacobsen <dana at acm.org>
Date:   Tue Jun 19 18:32:17 2012 -0600

    Thread safety
---
 Changes                |  2 +-
 XS.xs                  |  8 +++++---
 cache.c                | 21 ++++++++++++++-------
 cache.h                | 26 +++++++++++++++++++++-----
 lib/Math/Prime/Util.pm |  6 ++----
 sieve.c                |  1 +
 util.c                 | 45 +++++++++++++++++++++++++++++++--------------
 7 files changed, 75 insertions(+), 34 deletions(-)

diff --git a/Changes b/Changes
index cf964d2..4687a08 100644
--- a/Changes
+++ b/Changes
@@ -1,7 +1,7 @@
 Revision history for Perl extension Math::Prime::Util.
 
 0.08  20 June 2012
-    - Added thread scaffolding.
+    - Added thread safety, though with poor concurrency.
     - Accuracy improvement and measurements for math functions.
     - Remove simple sieve -- it wasn't being used, and was just around for
       performance comparisons.
diff --git a/XS.xs b/XS.xs
index f7a76c9..929a76d 100644
--- a/XS.xs
+++ b/XS.xs
@@ -73,7 +73,7 @@ prev_prime(IN UV n)
 UV
 _get_prime_cache_size()
   CODE:
-    RETVAL = get_prime_cache_size();
+    RETVAL = get_prime_cache(0, 0);
   OUTPUT:
     RETVAL
 
@@ -93,6 +93,7 @@ sieve_primes(IN UV low, IN UV high)
   CODE:
     if (low <= high) {
       if (get_prime_cache(high, &sieve) < high) {
+        release_prime_cache(sieve);
         croak("Could not generate sieve for %"UVuf, high);
       } else {
         if ((low <= 2) && (high >= 2)) { av_push(av, newSVuv( 2 )); }
@@ -102,6 +103,7 @@ sieve_primes(IN UV low, IN UV high)
         START_DO_FOR_EACH_SIEVE_PRIME( sieve, low, high ) {
            av_push(av,newSVuv(p));
         } END_DO_FOR_EACH_SIEVE_PRIME
+        release_prime_cache(sieve);
       }
     }
     RETVAL = newRV_noinc( (SV*) av );
@@ -166,7 +168,7 @@ segment_primes(IN UV low, IN UV high);
 
         /* Sieve from startd*30+1 to endd*30+29.  */
         if (sieve_segment(sieve, low_d, seghigh_d) == 0) {
-          free_prime_segment(sieve);
+          release_prime_segment(sieve);
           croak("Could not segment sieve from %"UVuf" to %"UVuf, segbase+1, seghigh);
         }
 
@@ -177,7 +179,7 @@ segment_primes(IN UV low, IN UV high);
         low_d += range_d;
         low = seghigh+2;
       }
-      free_prime_segment(sieve);
+      release_prime_segment(sieve);
     }
     RETVAL = newRV_noinc( (SV*) av );
   OUTPUT:
diff --git a/cache.c b/cache.c
index e0730f4..cebfde4 100644
--- a/cache.c
+++ b/cache.c
@@ -17,8 +17,8 @@
  *
  * Since we're trying to be thread-safe (and ideally allow a good deal
  * of concurrency), it is imperative these be used correctly.  You need
- * to call the get method, do your stuff, then call free.  Do *not* return
- * out of your function or croak without calling free.
+ * to call the get method, do your stuff, then call release.  Do *not* return
+ * out of your function or croak without calling release.
  */
 
 static int mutex_init = 0;
@@ -30,9 +30,6 @@ static perl_mutex primary_mutex;
 static unsigned char* prime_cache_sieve = 0;
 static UV             prime_cache_size = 0;
 
-/* Get the maximum sieved value of the cached prime sieve. */
-UV get_prime_cache_size(void) { return prime_cache_size; }
-
 /*
  * Get the size and a pointer to the cached prime sieve.
  * Returns the maximum sieved value in the sieve.
@@ -42,6 +39,8 @@ UV get_prime_cache_size(void) { return prime_cache_size; }
  */
 UV get_prime_cache(UV n, const unsigned char** sieve)
 {
+  MUTEX_LOCK(&primary_mutex);
+
   if (prime_cache_size < n) {
 
     if (prime_cache_sieve != 0)
@@ -62,10 +61,18 @@ UV get_prime_cache(UV n, const unsigned char** sieve)
       prime_cache_size = n;
   }
 
-  if (sieve != 0)
+  if (sieve == 0) {
+    MUTEX_UNLOCK(&primary_mutex);
+  } else {
     *sieve = prime_cache_sieve;
+  }
+
   return prime_cache_size;
 }
+void release_prime_cache(const unsigned char* mem) {
+  /* Thanks for letting us know you're done. */
+  MUTEX_UNLOCK(&primary_mutex);
+}
 
 
 
@@ -85,7 +92,7 @@ unsigned char* get_prime_segment(UV *size) {
   *size = SEGMENT_CHUNK_SIZE;
   return prime_segment;
 }
-void free_prime_segment(unsigned char* mem) {
+void release_prime_segment(unsigned char* mem) {
   /* Thanks for letting us know you're done. */
   MUTEX_UNLOCK(&segment_mutex);
 }
diff --git a/cache.h b/cache.h
index b7a6da3..97592ca 100644
--- a/cache.h
+++ b/cache.h
@@ -4,14 +4,30 @@
 #include "EXTERN.h"
 #include "perl.h"
 
-extern UV  get_prime_cache_size(void);
-extern UV  get_prime_cache(UV n, const unsigned char** sieve);
-extern void free_prime_cache(unsigned char* sieve);
-
+  /* Sieve from 0 to x and store in primary cache */
 extern void  prime_precalc(UV x);
+  /* Release all extra memory -- go back to initial amounts */
 extern void  prime_memfree(void);
 
+  /* Get the primary cache.  Returns the size.  Tries to make it
+   * at least size n.  Sets sieve* to the cache, unless given 0.
+   *
+   * Ex: just give me the current size:
+   *   UV cache_size = get_prime_cache(0, 0);
+   *
+   * Ex: give me the current cache and size:
+   *   UV cache_size = get_prime_cache(0, &sieve);
+   *
+   * Ex: give me the cache at least size n:
+   *   UV cache_size = get_prime_cache(n, &sieve);
+   */
+extern UV   get_prime_cache(UV n, const unsigned char** sieve);
+  /* Inform the system we're done using the primary cache if we got a ptr. */
+extern void release_prime_cache(const unsigned char* sieve);
+
+  /* Get the segment cache.  Set size to its size. */
 extern unsigned char* get_prime_segment(UV* size);
-extern void free_prime_segment(unsigned char* segment);
+  /* Inform the system we're done using the segment cache. */
+extern void release_prime_segment(unsigned char* segment);
 
 #endif
diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index c83c531..378fdf7 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -749,10 +749,8 @@ Perl versions earlier than 5.8.0 have issues with 64-bit.  The test suite will
 try to determine if your Perl is broken.  This will show up in factoring tests.
 Perl 5.6.2 32-bit works fine, as do later versions with 32-bit and 64-bit.
 
-Because static caches are used, many functions are not threadsafe.  If you
-use C<prime_precalc> and all calls have inputs smaller than that number,
-then only C<nth_prime> is problematic.  This will be addressed in a later
-implementation.
+The module is thread-safe, but will not currently allow much concurrency.  This
+is being worked on.
 
 
 =head1 PERFORMANCE
diff --git a/sieve.c b/sieve.c
index 61931b4..4372b9d 100644
--- a/sieve.c
+++ b/sieve.c
@@ -250,5 +250,6 @@ int sieve_segment(unsigned char* mem, UV startd, UV endd)
   }
   END_DO_FOR_EACH_SIEVE_PRIME;
 
+  release_prime_cache(sieve);
   return 1;
 }
diff --git a/util.c b/util.c
index 6b7e728..9556180 100644
--- a/util.c
+++ b/util.c
@@ -91,6 +91,7 @@ int is_prime(UV n)
   UV d, m;
   unsigned char mtab;
   const unsigned char* sieve;
+  int isprime;
 
   if ( n < (NPRIME_IS_SMALL*8))
     return ((prime_is_small[n/8] >> (n%8)) & 1) ? 2 : 0;
@@ -103,10 +104,12 @@ int is_prime(UV n)
   if (mtab == 0)
     return 0;
 
-  if (n <= get_prime_cache(0, &sieve))
-    return ((sieve[d] & mtab) == 0) ? 2 : 0;
+  isprime = (n <= get_prime_cache(0, &sieve))
+            ?  2*((sieve[d] & mtab) == 0)
+            :  -1;
+  release_prime_cache(sieve);
 
-  return _is_prime7(n);
+  return (isprime >= 0)  ?  isprime  :  _is_prime7(n);
 }
 
 /* Shortcut, asking for a very quick response of 1 = prime, 0 = dunno.
@@ -117,6 +120,7 @@ int is_definitely_prime(UV n)
   UV d, m;
   unsigned char mtab;
   const unsigned char* sieve;
+  int isprime;
 
   if ( n < (NPRIME_IS_SMALL*8))
     return ((prime_is_small[n/8] >> (n%8)) & 1);
@@ -129,8 +133,11 @@ int is_definitely_prime(UV n)
   if (mtab == 0)
     return 0;
 
-  if (n <= get_prime_cache(0, &sieve))
-    return ((sieve[d] & mtab) == 0);
+  isprime = (n <= get_prime_cache(0, &sieve))
+            ?  ((sieve[d] & mtab) == 0)
+            :  -1;
+  release_prime_cache(sieve);
+  if (isprime >= 0)  return isprime;
 
   if (n > MPU_PROB_PRIME_BEST)
     return (is_prob_prime(n) == 2);
@@ -182,11 +189,12 @@ UV next_prime(UV n)
   sieve_size = get_prime_cache(0, &sieve);
   if (n < sieve_size) {
     START_DO_FOR_EACH_SIEVE_PRIME(sieve, n+1, sieve_size)
-      return p;
+      { release_prime_cache(sieve); return p; }
     END_DO_FOR_EACH_SIEVE_PRIME;
     /* Not found, so must be larger than the cache size */
     n = sieve_size;
   }
+  release_prime_cache(sieve);
 
   d = n/30;
   m = n - d*30;
@@ -216,11 +224,15 @@ UV prev_prime(UV n)
   sieve_size = get_prime_cache(0, &sieve);
   if (n < sieve_size) {
     do {
-      m = prevwheel30[m];  if (m==29) { if (d == 0) return 0;  d--; }
+      m = prevwheel30[m];
+      if (m==29) { MPUassert(d>0, "d 0 in prev_prime");  d--; }
     } while (sieve[d] & masktab30[m]);
+    release_prime_cache(sieve);
   } else {
+    release_prime_cache(sieve);
     do {
-      m = prevwheel30[m];  if (m==29) { if (d == 0) return 0;  d--; }
+      m = prevwheel30[m];
+      if (m==29) { MPUassert(d>0, "d 0 in prev_prime");  d--; }
     } while (!_is_prime7(d*30+m));
   }
   return(d*30+m);
@@ -535,6 +547,7 @@ UV prime_count(UV low, UV high)
   if (segment_size < high_d) {
     /* Expand sieve to sqrt(n) */
     UV endp = (high_d >= (UV_MAX/30))  ?  UV_MAX-2  :  30*high_d+29;
+    release_prime_cache(cache_sieve);
     segment_size = get_prime_cache( sqrt(endp) + 1 , &cache_sieve) / 30;
   }
 
@@ -542,13 +555,16 @@ UV prime_count(UV low, UV high)
     /* Count all the primes in the primary cache in our range */
     count += count_segment_ranged(cache_sieve, segment_size, low, high);
 
-    if (high_d <= segment_size)
+    if (high_d <= segment_size) {
+      release_prime_cache(cache_sieve);
       return count;
+    }
 
     low_d = segment_size;
   }
+  release_prime_cache(cache_sieve);
 
-  /* More primes needed.  Repeatedly segment sieve */
+  /* More primes needed.  Repeatedly segment sieve. */
   segment = get_prime_segment(&segment_size);
   if (segment == 0)
     croak("Could not get segment memory");
@@ -563,7 +579,7 @@ UV prime_count(UV low, UV high)
     UV seghigh = (seghigh_d == high_d) ? high : (seghigh_d*30+29);
 
     if (sieve_segment(segment, low_d, seghigh_d) == 0) {
-      free_prime_segment(segment);
+      release_prime_segment(segment);
       croak("Could not segment sieve from %"UVuf" to %"UVuf, low_d*30+1, 30*seghigh_d+29);
     }
 
@@ -571,7 +587,7 @@ UV prime_count(UV low, UV high)
 
     low_d += range_d;
   }
-  free_prime_segment(segment);
+  release_prime_segment(segment);
 
   return count;
 }
@@ -748,6 +764,7 @@ UV nth_prime(UV n)
 
   /* Count up everything in the cached sieve. */
   count += count_segment_maxcount(cache_sieve, segment_size, target, &p);
+  release_prime_cache(cache_sieve);
   if (count == target)
     return p;
 
@@ -764,7 +781,7 @@ UV nth_prime(UV n)
 
     /* Do the actual sieving in the range */
     if (sieve_segment(segment, segbase, segbase + segment_size-1) == 0) {
-      free_prime_segment(segment);
+      release_prime_segment(segment);
       croak("Could not segment sieve from %"UVuf" to %"UVuf, 30*segbase+1, 30*(segbase+segment_size)+29);
     }
 
@@ -774,7 +791,7 @@ UV nth_prime(UV n)
     if (count < target)
       segbase += segment_size;
   }
-  free_prime_segment(segment);
+  release_prime_segment(segment);
   MPUassert(count == target, "nth_prime got incorrect count");
   return ( (segbase*30) + p );
 }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libmath-prime-util-perl.git



More information about the Pkg-perl-cvs-commits mailing list