[libmath-prime-util-perl] 02/08: Switch to pthreads condition variables
Partha P. Mukherjee
ppm-guest at moszumanska.debian.org
Thu May 21 18:47:21 UTC 2015
This is an automated email from the git hooks/post-receive script.
ppm-guest pushed a commit to annotated tag v0.22
in repository libmath-prime-util-perl.
commit 7c8fa2148769dd3f873866f0e3722d50979f5b9d
Author: Dana Jacobsen <dana at acm.org>
Date: Mon Feb 25 15:03:55 2013 -0800
Switch to pthreads condition variables
---
Changes | 2 +
cache.c | 153 +++++++++++++++++++++++++++++++++++-----------------------------
2 files changed, 87 insertions(+), 68 deletions(-)
diff --git a/Changes b/Changes
index 91c7e2d..c9591c1 100644
--- a/Changes
+++ b/Changes
@@ -8,6 +8,8 @@ Revision history for Perl extension Math::Prime::Util.
- Ranged totient uses less memory when segmented.
+ - Switch thread locking to pthreads condition variables.
+
0.21 22 February 2012
- Switch to using Bytes::Random::Secure for random primes. This is a
diff --git a/cache.c b/cache.c
index f815ad5..c6b9d2a 100644
--- a/cache.c
+++ b/cache.c
@@ -23,14 +23,60 @@
*/
static int mutex_init = 0;
-#ifdef USE_ITHREADS
-static perl_mutex segment_mutex;
+#ifndef USE_ITHREADS
+
+ #define WRITE_LOCK_START
+ #define WRITE_LOCK_END
+ #define READ_LOCK_START
+ #define READ_LOCK_END
+
+#else
+
+ static perl_mutex segment_mutex;
+ static perl_mutex primary_cache_mutex;
+ static perl_cond primary_cache_turn;
+ static int primary_cache_reading;
+ static int primary_cache_writing;
+ static int primary_cache_writers;
+
+ #define WRITE_LOCK_START \
+ do { \
+ MUTEX_LOCK(&primary_cache_mutex); \
+ primary_cache_writers++; \
+ while (primary_cache_reading || primary_cache_writing) \
+ COND_WAIT(&primary_cache_turn, &primary_cache_mutex); \
+ primary_cache_writing++; \
+ MUTEX_UNLOCK(&primary_cache_mutex); \
+ } while (0)
+
+ #define WRITE_LOCK_END \
+ do { \
+ MUTEX_LOCK(&primary_cache_mutex); \
+ primary_cache_writing--; \
+ primary_cache_writers--; \
+ COND_BROADCAST(&primary_cache_turn); \
+ MUTEX_UNLOCK(&primary_cache_mutex); \
+ } while (0)
+
+ #define READ_LOCK_START \
+ do { \
+ MUTEX_LOCK(&primary_cache_mutex); \
+ if (primary_cache_writers) \
+ COND_WAIT(&primary_cache_turn, &primary_cache_mutex); \
+ while (primary_cache_writing) \
+ COND_WAIT(&primary_cache_turn, &primary_cache_mutex); \
+ primary_cache_reading++; \
+ MUTEX_UNLOCK(&primary_cache_mutex); \
+ } while (0)
+
+ #define READ_LOCK_END \
+ do { \
+ MUTEX_LOCK(&primary_cache_mutex); \
+ primary_cache_reading--; \
+ COND_BROADCAST(&primary_cache_turn); \
+ MUTEX_UNLOCK(&primary_cache_mutex); \
+ } while (0)
-/* See: http://en.wikipedia.org/wiki/Readers-writers_problem */
-static perl_mutex primary_mutex_no_waiting;
-static perl_mutex primary_mutex_no_accessing;
-static perl_mutex primary_mutex_counter;
-static int primary_number_of_readers = 0;
#endif
static unsigned char* prime_cache_sieve = 0;
@@ -40,15 +86,9 @@ static UV prime_cache_size = 0;
#define FILL_EXTRA_N (128*30)
/* Erase the primary cache and fill up to n. */
+/* Note: You must have a write lock before calling this! */
static void _erase_and_fill_prime_cache(UV n) {
UV padded_n;
- /* Note: You need to handle mutexes around this.
- * MUTEX_LOCK(&primary_mutex_no_waiting);
- * MUTEX_LOCK(&primary_mutex_no_accessing);
- * MUTEX_UNLOCK(&primary_mutex_no_waiting);
- * _fill_prime_cache(n);
- * MUTEX_UNLOCK(&primary_mutex_no_accessing);
- */
if (n >= (UV_MAX-FILL_EXTRA_N))
padded_n = UV_MAX;
@@ -81,41 +121,31 @@ static void _erase_and_fill_prime_cache(UV n) {
UV get_prime_cache(UV n, const unsigned char** sieve)
{
#ifdef USE_ITHREADS
- int prev_readers;
- int i_hold_access_lock = 0;
-
if (sieve == 0) {
if (prime_cache_size < n) {
- MUTEX_LOCK(&primary_mutex_no_waiting);
- MUTEX_LOCK(&primary_mutex_no_accessing);
- MUTEX_UNLOCK(&primary_mutex_no_waiting);
- _erase_and_fill_prime_cache(n);
- MUTEX_UNLOCK(&primary_mutex_no_accessing);
+ WRITE_LOCK_START;
+ _erase_and_fill_prime_cache(n);
+ WRITE_LOCK_END;
}
return prime_cache_size;
}
- MUTEX_LOCK(&primary_mutex_no_waiting);
- /* If we need more primes, get the access lock right now */
- if (prime_cache_size < n) {
- MUTEX_LOCK(&primary_mutex_no_accessing);
- i_hold_access_lock = 1;
- }
-
- MUTEX_LOCK(&primary_mutex_counter);
- prev_readers = primary_number_of_readers;
- primary_number_of_readers++;
- MUTEX_UNLOCK(&primary_mutex_counter);
-
- if ( (prev_readers == 0) && (!i_hold_access_lock) ) {
- MUTEX_LOCK(&primary_mutex_no_accessing);
- i_hold_access_lock = 1;
- }
- if (prime_cache_size < n) {
- _erase_and_fill_prime_cache(n);
- }
- MUTEX_UNLOCK(&primary_mutex_no_waiting);
-
+ /* This could be done more efficiently if we converted a write lock to a
+ * reader after doing the expansion. But I think this solution is less
+ * error prone (though could lead to starvation in pathological cases).
+ */
+ READ_LOCK_START;
+ while (prime_cache_size < n) {
+ /* The cache isn't big enough. Expand it. */
+ READ_LOCK_END;
+ /* thread reminder: the world can change right here */
+ WRITE_LOCK_START;
+ if (prime_cache_size < n)
+ _erase_and_fill_prime_cache(n);
+ WRITE_LOCK_END;
+ /* thread reminder: the world can change right here */
+ READ_LOCK_START;
+ }
MPUassert(prime_cache_size >= n, "prime cache is too small!");
*sieve = prime_cache_sieve;
@@ -130,14 +160,8 @@ UV get_prime_cache(UV n, const unsigned char** sieve)
}
void release_prime_cache(const unsigned char* mem) {
-#ifdef USE_ITHREADS
- int current_readers;
- MUTEX_LOCK(&primary_mutex_counter);
- primary_number_of_readers--;
- current_readers = primary_number_of_readers;
- MUTEX_UNLOCK(&primary_mutex_counter);
- if (current_readers == 0) { MUTEX_UNLOCK(&primary_mutex_no_accessing); }
-#endif
+ (void)mem; /* We don't currently care about the pointer */
+ READ_LOCK_END;
}
@@ -151,7 +175,7 @@ static int prime_segment_is_available = 1;
unsigned char* get_prime_segment(UV *size) {
unsigned char* mem;
- int use_prime_segment;
+ int use_prime_segment = 0;
MPUassert(size != 0, "get_prime_segment given null size pointer");
MPUassert(mutex_init == 1, "segment mutex has not been initialized");
@@ -160,8 +184,6 @@ unsigned char* get_prime_segment(UV *size) {
if (prime_segment_is_available) {
prime_segment_is_available = 0;
use_prime_segment = 1;
- } else {
- use_prime_segment = 0;
}
MUTEX_UNLOCK(&segment_mutex);
@@ -185,10 +207,11 @@ void release_prime_segment(unsigned char* mem) {
MUTEX_LOCK(&segment_mutex);
if (mem == prime_segment) {
prime_segment_is_available = 1;
- } else {
- Safefree(mem);
+ mem = 0;
}
MUTEX_UNLOCK(&segment_mutex);
+ if (mem)
+ Safefree(mem);
}
@@ -197,10 +220,8 @@ void release_prime_segment(unsigned char* mem) {
void prime_precalc(UV n)
{
if (!mutex_init) {
- MUTEX_INIT(&segment_mutex);
- MUTEX_INIT(&primary_mutex_no_waiting);
- MUTEX_INIT(&primary_mutex_no_accessing);
- MUTEX_INIT(&primary_mutex_counter);
+ MUTEX_INIT(&primary_cache_mutex);
+ COND_INIT(&primary_cache_turn);
mutex_init = 1;
}
@@ -225,12 +246,10 @@ void prime_memfree(void)
}
MUTEX_UNLOCK(&segment_mutex);
- MUTEX_LOCK(&primary_mutex_no_waiting);
- MUTEX_LOCK(&primary_mutex_no_accessing);
- MUTEX_UNLOCK(&primary_mutex_no_waiting);
+ WRITE_LOCK_START;
/* Put primary cache back to initial state */
_erase_and_fill_prime_cache(INITIAL_CACHE_SIZE);
- MUTEX_UNLOCK(&primary_mutex_no_accessing);
+ WRITE_LOCK_END;
}
@@ -238,10 +257,8 @@ void _prime_memfreeall(void)
{
/* No locks. We're shutting everything down. */
if (mutex_init) {
- MUTEX_DESTROY(&segment_mutex);
- MUTEX_DESTROY(&primary_mutex_no_waiting);
- MUTEX_DESTROY(&primary_mutex_no_accessing);
- MUTEX_DESTROY(&primary_mutex_counter);
+ MUTEX_DESTROY(&primary_cache_mutex);
+ COND_DESTROY(&primary_cache_turn);
mutex_init = 0;
}
if (prime_cache_sieve != 0)
--
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