unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
From: "Julian Graham" <joolean@gmail.com>
To: "Neil Jerram" <neil@ossau.uklinux.net>
Cc: "Ludovic Courtès" <ludo@gnu.org>, guile-devel@gnu.org
Subject: Re: srfi-18 requirements
Date: Sun, 13 Apr 2008 20:43:26 -0400	[thread overview]
Message-ID: <2bc5f8210804131743p10e3a24bu15a4fb1985f72d1b@mail.gmail.com> (raw)
In-Reply-To: <87tziaogxp.fsf@ossau.uklinux.net>

[-- Attachment #1: Type: text/plain, Size: 1655 bytes --]

Hi Neil,

(Apologies for the delay in getting this to you -- a host of
professional and personal crises reared up over the last week or so.)

Find attached a patch (in two parts -- sorry, still getting used to
git) to the core threading code that includes the changes we
discussed, specifically:

* Re-added support for locking mutexes with an owner other than the
current thread
* Enabled the previously ifdef'd out functions scm_mutex_owner and
scm_mutex_level
* Added a new function, scm_mutex_locked_p, useful for distinguishing
between unlocked and unowned mutex states.
* Updated the threads.test file to reflect the changes above
* Updated the documentation in api-scheduling.texi to reflect the changes above
* Updated the ChangeLog to reflect the changes above

Also attached are updated versions of the Scheme SRFI-18
implementation as well as the SRFI-18-specific test code.

A couple of notes: For purposes of elegance, I've changed semantics of
fat_mutex.level -- where previously all non-recursive mutexes had a
level of -1, recursiveness is now denoted by an integer field on
fat_mutex.  Any mutex (recursive or not) is in a locked state iff its
level is greater than 0.

Second, during the testing I did for this round of changes, I noticed
a few more deadlocks that I believe are related to the existing core
threading model (as opposed to the changes included in this patch).
These seem to crop up when I run overnight tests on modified core
code, probably because different timings and thread interactions are
introduced.  I think I've got fixes for some of them, and I'll
follow-up in a separate mailing list thread.


Regards,
Julian

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-latest-set-of-SRFI-18-support-changes-to-core-thread.patch --]
[-- Type: text/x-diff; name=0001-latest-set-of-SRFI-18-support-changes-to-core-thread.patch, Size: 14332 bytes --]

From 4f04a30ccb5f4129c0720f6b10bbad3ef9244cb6 Mon Sep 17 00:00:00 2001
From: Julian Graham <julian@smokebottle.(none)>
Date: Sun, 13 Apr 2008 19:51:23 -0400
Subject: [PATCH] latest set of SRFI-18 support changes to core threads

---
 doc/ref/api-scheduling.texi   |   33 +++++++-
 libguile/threads.c            |  162 ++++++++++++++++++++++------------------
 libguile/threads.h            |    5 +-
 test-suite/tests/threads.test |   46 ++++++++++++
 4 files changed, 168 insertions(+), 78 deletions(-)

diff --git a/doc/ref/api-scheduling.texi b/doc/ref/api-scheduling.texi
index ec136fb..29eed5e 100644
--- a/doc/ref/api-scheduling.texi
+++ b/doc/ref/api-scheduling.texi
@@ -409,17 +409,21 @@ function is equivalent to calling `make-mutex' and specifying the
 @code{recursive} flag.
 @end deffn
 
-@deffn {Scheme Procedure} lock-mutex mutex [timeout]
+@deffn {Scheme Procedure} lock-mutex mutex [timeout [owner]]
 @deffnx {C Function} scm_lock_mutex (mutex)
-@deffnx {C Function} scm_lock_mutex_timed (mutex, timeout)
-Lock @var{mutex}.  If the mutex is already locked by another thread
-then block and return only when @var{mutex} has been acquired.
+@deffnx {C Function} scm_lock_mutex_timed (mutex, timeout, owner)
+Lock @var{mutex}.  If the mutex is already locked, then block and 
+return only when @var{mutex} has been acquired.
 
 When @var{timeout} is given, it specifies a point in time where the 
 waiting should be aborted.  It can be either an integer as returned 
 by @code{current-time} or a pair as returned by @code{gettimeofday}.  
 When the waiting is aborted, @code{#f} is returned. 
 
+When @var{owner} is given, it specifies an owner for @var{mutex} other
+than the calling thread.  @var{owner} may also be @code{#f}, 
+indicating that the mutex should be locked but left unowned.
+
 For standard mutexes (@code{make-mutex}), and error is signalled if
 the thread has itself already locked @var{mutex}.
 
@@ -471,6 +475,27 @@ returned by @code{current-time} or a pair as returned by
 returned.  Otherwise the function returns @code{#t}.
 @end deffn
 
+@deffn {Scheme Procedure} mutex-owner mutex
+@deffnx {C Function} scm_mutex_owner (mutex)
+Return the current owner of @var{mutex}, in the form of a thread or 
+@code{#f} (indicating no owner).  Note that a mutex may be unowned but
+still locked.
+@end deffn
+
+@deffn {Scheme Procedure} mutex-level mutex
+@deffnx {C Function} scm_mutex_level (mutex)
+Return the current lock level of @var{mutex}.  If @var{mutex} is
+currently unlocked, this value will be 0; otherwise, it will be the
+number of times @var{mutex} has been recursively locked by its current
+owner.
+@end deffn
+
+@deffn {Scheme Procedure} mutex-locked? mutex
+@deffnx {C Function} scm_mutex_locked_p (mutex)
+Return @code{#t} if @var{mutex} is locked, regardless of ownership;
+otherwise, return @code{#f}.
+@end deffn
+
 @deffn {Scheme Procedure} make-condition-variable
 @deffnx {C Function} scm_make_condition_variable ()
 Return a new condition variable.
diff --git a/libguile/threads.c b/libguile/threads.c
index 68c5f79..938644c 100644
--- a/libguile/threads.c
+++ b/libguile/threads.c
@@ -511,9 +511,9 @@ guilify_self_2 (SCM parent)
 typedef struct {
   scm_i_pthread_mutex_t lock;
   SCM owner;
-  int level;      /* how much the owner owns us.  
-		     < 0 for non-recursive mutexes */
+  int level; /* how much the owner owns us.  <= 1 for non-recursive mutexes */
 
+  int recursive; /* allow recursive locking? */
   int unchecked_unlock; /* is it an error to unlock an unlocked mutex? */
   int allow_external_unlock; /* is it an error to unlock a mutex that is not
 				owned by the current thread? */
@@ -1154,8 +1154,9 @@ make_fat_mutex (int recursive, int unchecked_unlock, int external_unlock)
   m = scm_gc_malloc (sizeof (fat_mutex), "mutex");
   scm_i_pthread_mutex_init (&m->lock, NULL);
   m->owner = SCM_BOOL_F;
-  m->level = recursive? 0 : -1;
+  m->level = 0;
 
+  m->recursive = recursive;
   m->unchecked_unlock = unchecked_unlock;
   m->allow_external_unlock = external_unlock;
 
@@ -1211,79 +1212,77 @@ SCM_DEFINE (scm_make_recursive_mutex, "make-recursive-mutex", 0, 0, 0,
 SCM_SYMBOL (scm_abandoned_mutex_error_key, "abandoned-mutex-error");
 
 static SCM
-fat_mutex_lock (SCM mutex, scm_t_timespec *timeout, int *ret)
+fat_mutex_lock (SCM mutex, scm_t_timespec *timeout, SCM owner, int *ret)
 {
   fat_mutex *m = SCM_MUTEX_DATA (mutex);
 
-  SCM thread = scm_current_thread ();
-  scm_i_thread *t = SCM_I_THREAD_DATA (thread);
-
+  SCM new_owner = SCM_UNBNDP (owner) ? scm_current_thread() : owner;
   SCM err = SCM_BOOL_F;
 
   struct timeval current_time;
 
   scm_i_scm_pthread_mutex_lock (&m->lock);
-  if (scm_is_false (m->owner))
-    {
-      m->owner = thread;
-      scm_i_pthread_mutex_lock (&t->admin_mutex);
-      t->mutexes = scm_cons (mutex, t->mutexes);
-      scm_i_pthread_mutex_unlock (&t->admin_mutex);
-      *ret = 1;
-    }
-  else if (scm_is_eq (m->owner, thread))
+
+  while (1)
     {
-      if (m->level >= 0)
+      if (m->level == 0)
 	{
+	  m->owner = new_owner;
 	  m->level++;
-	  *ret = 1;
-	}
-      else
-	err = scm_cons (scm_misc_error_key,
-			scm_from_locale_string ("mutex already locked by "
-						"current thread"));
-    }
-  else
-    {
-      int first_iteration = 1;
-      while (1)
-	{
-	  if (scm_is_eq (m->owner, thread) || scm_c_thread_exited_p (m->owner))
+	  
+	  if (SCM_I_IS_THREAD (new_owner))
 	    {
+	      scm_i_thread *t = SCM_I_THREAD_DATA (new_owner);
 	      scm_i_pthread_mutex_lock (&t->admin_mutex);
 	      t->mutexes = scm_cons (mutex, t->mutexes);
 	      scm_i_pthread_mutex_unlock (&t->admin_mutex);
-	      *ret = 1;
-	      if (scm_c_thread_exited_p (m->owner)) 
-		{
-		  m->owner = thread;
-		  err = scm_cons (scm_abandoned_mutex_error_key,
-				  scm_from_locale_string ("lock obtained on "
-							  "abandoned mutex"));
-		}
-	      break;
 	    }
-	  else if (!first_iteration)
+	  *ret = 1;
+	  break;
+	}
+      else if (SCM_I_IS_THREAD (m->owner) && scm_c_thread_exited_p (m->owner))
+	{
+	  m->owner = new_owner;
+	  err = scm_cons (scm_abandoned_mutex_error_key,
+			  scm_from_locale_string ("lock obtained on abandoned "
+						  "mutex"));
+	  *ret = 1;
+	  break;
+	}
+      else if (scm_is_eq (m->owner, new_owner))
+	{
+	  if (m->recursive)
+	    {
+	      m->level++;
+	      *ret = 1;		  
+	    }
+	  else
 	    {
-	      if (timeout != NULL) 
+	      err = scm_cons (scm_misc_error_key,
+			      scm_from_locale_string ("mutex already locked "
+						      "by thread"));
+	      *ret = 0;
+	    }
+	  break;  
+	}
+      else
+	{
+	  if (timeout != NULL) 
+	    {
+	      gettimeofday (&current_time, NULL);
+	      if (current_time.tv_sec > timeout->tv_sec ||
+		  (current_time.tv_sec == timeout->tv_sec &&
+		   current_time.tv_usec * 1000 > timeout->tv_nsec))
 		{
-		  gettimeofday (&current_time, NULL);
-		  if (current_time.tv_sec > timeout->tv_sec ||
-		      (current_time.tv_sec == timeout->tv_sec &&
-		       current_time.tv_usec * 1000 > timeout->tv_nsec))
-		    {
-		      *ret = 0;
-		      break;
-		    }
+		  *ret = 0;
+		  break;
 		}
-	      scm_i_pthread_mutex_unlock (&m->lock);
-	      SCM_TICK;
-	      scm_i_scm_pthread_mutex_lock (&m->lock);
 	    }
-	  else
-	    first_iteration = 0;
-	  block_self (m->waiting, mutex, &m->lock, timeout);
+	  scm_i_pthread_mutex_unlock (&m->lock);
+	  SCM_TICK;
+	  scm_i_scm_pthread_mutex_lock (&m->lock);
 	}
+      block_self (m->waiting, mutex, &m->lock, timeout);
     }
   scm_i_pthread_mutex_unlock (&m->lock);
   return err;
@@ -1291,11 +1290,11 @@ fat_mutex_lock (SCM mutex, scm_t_timespec *timeout, int *ret)
 
 SCM scm_lock_mutex (SCM mx)
 {
-  return scm_lock_mutex_timed (mx, SCM_UNDEFINED);
+  return scm_lock_mutex_timed (mx, SCM_UNDEFINED, SCM_UNDEFINED);
 }
 
-SCM_DEFINE (scm_lock_mutex_timed, "lock-mutex", 1, 1, 0,
-	    (SCM m, SCM timeout),
+SCM_DEFINE (scm_lock_mutex_timed, "lock-mutex", 1, 2, 0,
+	    (SCM m, SCM timeout, SCM owner),
 "Lock @var{mutex}. If the mutex is already locked, the calling thread "
 "blocks until the mutex becomes available. The function returns when "
 "the calling thread owns the lock on @var{mutex}.  Locking a mutex that "
@@ -1315,7 +1314,7 @@ SCM_DEFINE (scm_lock_mutex_timed, "lock-mutex", 1, 1, 0,
       waittime = &cwaittime;
     }
 
-  exception = fat_mutex_lock (m, waittime, &ret);
+  exception = fat_mutex_lock (m, waittime, owner, &ret);
   if (!scm_is_false (exception))
     scm_ithrow (SCM_CAR (exception), scm_list_1 (SCM_CDR (exception)), 1);
   return ret ? SCM_BOOL_T : SCM_BOOL_F;
@@ -1346,7 +1345,7 @@ SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
   to_timespec (scm_from_int(0), &cwaittime);
   waittime = &cwaittime;
   
-  exception = fat_mutex_lock (mutex, waittime, &ret);
+  exception = fat_mutex_lock (mutex, waittime, SCM_UNDEFINED, &ret);
   if (!scm_is_false (exception))
     scm_ithrow (SCM_CAR (exception), scm_list_1 (SCM_CDR (exception)), 1);
   return ret ? SCM_BOOL_T : SCM_BOOL_F;
@@ -1373,15 +1372,19 @@ fat_mutex_unlock (SCM mutex, SCM cond,
   int err = 0, ret = 0;
 
   scm_i_scm_pthread_mutex_lock (&m->lock);
-  if (!scm_is_eq (m->owner, scm_current_thread ()))
+
+  SCM owner = m->owner;
+
+  if (!scm_is_eq (owner, scm_current_thread ()))
     {
-      if (scm_is_false (m->owner))
+      if (m->level == 0)
 	{
 	  if (!m->unchecked_unlock)
 	    {
 	      scm_i_pthread_mutex_unlock (&m->lock);
 	      scm_misc_error (NULL, "mutex not locked", SCM_EOL);
 	    }
+	  owner = scm_current_thread ();
 	}
       else if (!m->allow_external_unlock)
 	{
@@ -1392,8 +1395,6 @@ fat_mutex_unlock (SCM mutex, SCM cond,
 
   if (! (SCM_UNBNDP (cond)))
     {
-      int lock_ret = 0;
-
       c = SCM_CONDVAR_DATA (cond);
       while (1)
 	{
@@ -1402,8 +1403,9 @@ fat_mutex_unlock (SCM mutex, SCM cond,
 	  scm_i_scm_pthread_mutex_lock (&c->lock);
 	  if (m->level > 0)
 	    m->level--;
-	  else
+	  if (m->level == 0)
 	    m->owner = unblock_from_queue (m->waiting);
+
 	  scm_i_pthread_mutex_unlock (&m->lock);
 	  
 	  t->block_asyncs++;
@@ -1430,7 +1432,7 @@ fat_mutex_unlock (SCM mutex, SCM cond,
 	  if (brk)
 	    {
 	      if (relock)
-		fat_mutex_lock (mutex, NULL, &lock_ret);
+		scm_lock_mutex_timed (mutex, SCM_UNDEFINED, owner);
 	      scm_i_pthread_mutex_unlock (&c->lock);
 	      break;
 	    }
@@ -1449,8 +1451,9 @@ fat_mutex_unlock (SCM mutex, SCM cond,
     {
       if (m->level > 0)
 	m->level--;
-      else
+      if (m->level == 0) 
 	m->owner = unblock_from_queue (m->waiting);
+	  
       scm_i_pthread_mutex_unlock (&m->lock);
       ret = 1;
     }
@@ -1501,22 +1504,27 @@ SCM_DEFINE (scm_mutex_p, "mutex?", 1, 0, 0,
 }
 #undef FUNC_NAME 
 
-#if 0
-
 SCM_DEFINE (scm_mutex_owner, "mutex-owner", 1, 0, 0,
 	    (SCM mx),
 	    "Return the thread owning @var{mx}, or @code{#f}.")
 #define FUNC_NAME s_scm_mutex_owner
 {
+  SCM owner;
+  fat_mutex *m = NULL;
+
   SCM_VALIDATE_MUTEX (1, mx);
-  return (SCM_MUTEX_DATA(mx))->owner;
+  m = SCM_MUTEX_DATA (mx);
+  scm_i_pthread_mutex_lock (&m->lock);
+  owner = m->owner;
+  scm_i_pthread_mutex_unlock (&m->lock);
+
+  return owner;
 }
 #undef FUNC_NAME
 
 SCM_DEFINE (scm_mutex_level, "mutex-level", 1, 0, 0,
 	    (SCM mx),
-	    "Return the lock level of a recursive mutex, or -1\n"
-	    "for a standard mutex.")
+	    "Return the lock level of mutex @var{mx}.")
 #define FUNC_NAME s_scm_mutex_level
 {
   SCM_VALIDATE_MUTEX (1, mx);
@@ -1524,7 +1532,15 @@ SCM_DEFINE (scm_mutex_level, "mutex-level", 1, 0, 0,
 }
 #undef FUNC_NAME
 
-#endif
+SCM_DEFINE (scm_mutex_locked_p, "mutex-locked?", 1, 0, 0,
+	    (SCM mx),
+	    "Returns @code{#t} if the mutex @var{mx} is locked.")
+#define FUNC_NAME s_scm_mutex_locked_p
+{
+  SCM_VALIDATE_MUTEX (1, mx);
+  return SCM_MUTEX_DATA (mx)->level > 0 ? SCM_BOOL_T : SCM_BOOL_F;
+}
+#undef FUNC_NAME
 
 static SCM
 fat_cond_mark (SCM cv)
diff --git a/libguile/threads.h b/libguile/threads.h
index e1944a5..d10e0f8 100644
--- a/libguile/threads.h
+++ b/libguile/threads.h
@@ -170,12 +170,15 @@ SCM_API SCM scm_make_mutex (void);
 SCM_API SCM scm_make_recursive_mutex (void);
 SCM_API SCM scm_make_mutex_with_flags (SCM flags);
 SCM_API SCM scm_lock_mutex (SCM m);
-SCM_API SCM scm_lock_mutex_timed (SCM m, SCM timeout);
+SCM_API SCM scm_lock_mutex_timed (SCM m, SCM timeout, SCM owner);
 SCM_API void scm_dynwind_lock_mutex (SCM mutex);
 SCM_API SCM scm_try_mutex (SCM m);
 SCM_API SCM scm_unlock_mutex (SCM m);
 SCM_API SCM scm_unlock_mutex_timed (SCM m, SCM cond, SCM timeout);
 SCM_API SCM scm_mutex_p (SCM o);
+SCM_API SCM scm_mutex_locked_p (SCM m);
+SCM_API SCM scm_mutex_owner (SCM m);
+SCM_API SCM scm_mutex_level (SCM m);
 
 SCM_API SCM scm_make_condition_variable (void);
 SCM_API SCM scm_wait_condition_variable (SCM cond, SCM mutex);
diff --git a/test-suite/tests/threads.test b/test-suite/tests/threads.test
index 62ee0cd..9cd062d 100644
--- a/test-suite/tests/threads.test
+++ b/test-suite/tests/threads.test
@@ -279,6 +279,52 @@
 	  (not (thread-cleanup (current-thread)))))
 
       ;;
+      ;; mutex ownership
+      ;;
+
+      (with-test-prefix "mutex-ownership"
+	(pass-if "mutex ownership for locked mutex"
+	  (let ((m (make-mutex)))
+	    (lock-mutex m)
+	    (eq? (mutex-owner m) (current-thread))))
+
+	(pass-if "mutex ownership for unlocked mutex"
+	  (let ((m (make-mutex)))
+	    (not (mutex-owner m))))
+
+	(pass-if "locking mutex on behalf of other thread"
+	  (let* ((m (make-mutex))
+		 (t (begin-thread 'foo)))
+	    (lock-mutex m #f t)
+	    (eq? (mutex-owner m) t)))
+
+        (pass-if "locking mutex with no owner"
+	  (let ((m (make-mutex)))
+	    (lock-mutex m #f #f)
+	    (not (mutex-owner m)))))
+
+      ;;
+      ;; mutex lock levels
+      ;;
+
+      (with-test-prefix "mutex-lock-levels"
+			
+        (pass-if "unlocked level is 0"
+	  (let ((m (make-mutex)))
+	    (and (not (mutex-locked? m)) (eqv? (mutex-level m) 0))))
+
+        (pass-if "non-recursive lock level is 1"
+	  (let ((m (make-mutex)))
+	    (lock-mutex m)
+	    (and (mutex-locked? m) (eqv? (mutex-level m) 1))))
+
+	(pass-if "recursive lock level is >1"
+	  (let ((m (make-mutex 'recursive)))
+	    (lock-mutex m)
+	    (lock-mutex m)
+	    (and (mutex-locked? m) (eqv? (mutex-level m) 2)))))
+
+      ;;
       ;; mutex behavior
       ;;
 
-- 
1.5.4.3


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-ChangeLog-updates-for-latest-set-of-SRFI-18-changes.patch --]
[-- Type: text/x-diff; name=0002-ChangeLog-updates-for-latest-set-of-SRFI-18-changes.patch, Size: 2507 bytes --]

From e9a8373bd67f8b39ad378175931fa258f953a3ba Mon Sep 17 00:00:00 2001
From: Julian Graham <julian@smokebottle.(none)>
Date: Sun, 13 Apr 2008 20:31:18 -0400
Subject: [PATCH] ChangeLog updates for latest set of SRFI-18 changes

---
 doc/ref/ChangeLog    |    8 ++++++++
 libguile/ChangeLog   |   14 ++++++++++++++
 test-suite/ChangeLog |    9 +++++++++
 3 files changed, 31 insertions(+), 0 deletions(-)

diff --git a/doc/ref/ChangeLog b/doc/ref/ChangeLog
index 7719819..4a0a943 100644
--- a/doc/ref/ChangeLog
+++ b/doc/ref/ChangeLog
@@ -1,3 +1,11 @@
+2008-04-13  Julian Graham  <joolean@gmail.com>
+
+	* api-scheduling.texi (Mutexes and Condition Variables): Add
+	documentation for new functions "scm_mutex_owner",
+	"scm_mutex_level", and "scm_mutex_locked_p".  Update
+	documentation for function "scm_lock_mute_timed" to reflect
+	addition of optional ownership argument.
+
 2008-03-28  Neil Jerram  <neil@ossau.uklinux.net>
 
 	* libguile-concepts.texi (Multi-Threading): Fix typo.
diff --git a/libguile/ChangeLog b/libguile/ChangeLog
index ea75158..48a849e 100644
--- a/libguile/ChangeLog
+++ b/libguile/ChangeLog
@@ -1,3 +1,17 @@
+2008-04-13  Julian Graham  <joolean@gmail.com>
+
+	* threads.c (fat_mutex)[recursive]: New field.
+	(make_fat_mutex): Adjust initialization to reflect changes to
+	mutex lock level semantics.
+	(fat_mutex_lock, fat_mutex_unlock): Add support for unowned 
+	mutexes and locking mutexes on behalf of other threads.
+	(scm_lock_mutex, scm_lock_mutex_timed): Update to reflect
+	signature change to fat_mutex_lock.
+	(scm_mutex_owner, scm_mutex_level, scm_mutex_locked_p): New /
+	re-enabled functions.
+	* threads.h (scm_mutex_owner, scm_mutex_level, 
+	scm_mutex_locked_p): Prototypes for new functions.
+
 2008-04-13  Ludovic Courtès  <ludo@gnu.org>
 
 	* inline.h (SCM_C_USE_EXTERN_INLINE): New macro.  Use it to make
diff --git a/test-suite/ChangeLog b/test-suite/ChangeLog
index 7b546b3..fcacd9c 100644
--- a/test-suite/ChangeLog
+++ b/test-suite/ChangeLog
@@ -1,3 +1,12 @@
+2008-04-13  Julian Graham  <joolean@gmail.com>
+
+	* tests/threads.test (mutex-ownership, mutex-lock-levels): New 
+	test prefix.
+	(mutex ownership for locked mutex, mutex ownership for unlocked
+	mutex, locking mutex on behalf of other thread, locking mutex
+	with no owner, unlocked level is 0, non-recursive lock level
+	is 1, recursive lock level is >1): New tests.
+
 2008-04-13  Ludovic Courtès  <ludo@gnu.org>
 
 	* tests/goops.test (defining classes)[interaction with
-- 
1.5.4.3


[-- Attachment #4: srfi-18.scm --]
[-- Type: application/octet-stream, Size: 12057 bytes --]

;;; srfi-18.scm --- Multithreading support

;; Copyright (C) 2008 Free Software Foundation, Inc.
;;
;; This library is free software; you can redistribute it and/or
;; modify it under the terms of the GNU Lesser General Public
;; License as published by the Free Software Foundation; either
;; version 2.1 of the License, or (at your option) any later version.
;; 
;; This library is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; Lesser General Public License for more details.
;; 
;; You should have received a copy of the GNU Lesser General Public
;; License along with this library; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

;;; Author: Julian Graham <julian.graham@aya.yale.edu>
;;; Date: 2008-04-11

;;; Commentary:

;; This is an implementation of SRFI-18 (Multithreading support).
;;
;; All procedures defined in SRFI-18, which are not already defined in
;; the Guile core library, are exported.
;;
;; This module is fully documented in the Guile Reference Manual.

;;; Code:

(define-module (srfi srfi-18)
  :use-module (srfi srfi-34)
  :export (

;;; Threads
 ;; current-thread			<= in the core
 ;; thread?				<= in the core
 make-thread
 thread-name
 thread-specific
 thread-specific-set!
 thread-start!
 thread-yield!
 thread-sleep!
 thread-terminate!
 thread-join!

;;; Mutexes
 ;; mutex?				<= in the core
 make-mutex
 mutex-name
 mutex-specific
 mutex-specific-set!
 mutex-state
 mutex-lock!
 mutex-unlock!

;;; Condition variables
 ;; condition-variable?			<= in the core
 make-condition-variable
 condition-variable-name
 condition-variable-specific
 condition-variable-specific-set!
 condition-variable-signal!
 condition-variable-broadcast!
 condition-variable-wait!

;;; Time
 current-time
 time?
 time->seconds
 seconds->time
 
 current-exception-handler
 with-exception-handler
 raise
 join-timeout-exception?
 abandoned-mutex-exception?
 terminated-thread-exception?
 uncaught-exception?
 uncaught-exception-reason
 )
  :re-export (thread? mutex? condition-variable?)
  :replace (current-time 
	    make-thread 
	    make-mutex 
	    make-condition-variable
	    raise))

(cond-expand-provide (current-module) '(srfi-18))

(define (check-arg-type pred arg caller)
  (if (pred arg)
      arg
      (scm-error 'wrong-type-arg caller
		 "Wrong type argument: ~S" (list arg) '())))

(define abandoned-mutex-exception (list 'abandoned-mutex-exception))
(define join-timeout-exception (list 'join-timeout-exception))
(define terminated-thread-exception (list 'terminated-thread-exception))
(define uncaught-exception (list 'uncaught-exception))

(define mutex-owners (make-weak-key-hash-table))
(define object-names (make-weak-key-hash-table))
(define object-specifics (make-weak-key-hash-table))
(define thread-start-conds (make-weak-key-hash-table))
(define thread-exception-handlers (make-weak-key-hash-table))

;; EXCEPTIONS

(define raise (@ (srfi srfi-34) raise))
(define (initial-handler obj) 
  (srfi-18-exception-preserver (cons uncaught-exception obj)))

(define thread->exception (make-object-property))

(define (srfi-18-exception-preserver obj)
  (if (or (terminated-thread-exception? obj)
          (uncaught-exception? obj))
      (set! (thread->exception (current-thread)) obj)))

(define (srfi-18-exception-handler key . args)

  ;; SRFI 34 exceptions continue to bubble up no matter who handles them, so
  ;; if one is caught at this level, it has already been taken care of by
  ;; `initial-handler'.

  (and (not (eq? key 'srfi-34))
       (srfi-18-exception-preserver (if (null? args) 
					(cons uncaught-exception key)
					(cons* uncaught-exception key args)))))

(define (current-handler-stack)
  (let ((ct (current-thread)))
    (or (hashq-ref thread-exception-handlers ct)
	(hashq-set! thread-exception-handlers ct (list initial-handler)))))

(define (with-exception-handler handler thunk)
  (let ((ct (current-thread))
        (hl (current-handler-stack)))
    (check-arg-type procedure? handler "with-exception-handler") 
    (check-arg-type thunk? thunk "with-exception-handler")
    (hashq-set! thread-exception-handlers ct (cons handler hl))
    (apply (@ (srfi srfi-34) with-exception-handler) 
           (list (lambda (obj)
                   (hashq-set! thread-exception-handlers ct hl) 
                   (handler obj))
                 (lambda () 
                   (let ((r (thunk)))
                     (hashq-set! thread-exception-handlers ct hl) r))))))

(define (current-exception-handler)
  (car (current-handler-stack)))

(define (join-timeout-exception? obj) (eq? obj join-timeout-exception))
(define (abandoned-mutex-exception? obj) (eq? obj abandoned-mutex-exception))
(define (uncaught-exception? obj) 
  (and (pair? obj) (eq? (car obj) uncaught-exception)))
(define (uncaught-exception-reason exc)
  (cdr (check-arg-type uncaught-exception? exc "uncaught-exception-reason")))
(define (terminated-thread-exception? obj) 
  (eq? obj terminated-thread-exception))

;; THREADS

;; Create a new thread and prevent it from starting using a condition variable.
;; Once started, install a top-level exception handler that rethrows any 
;; exceptions wrapped in an uncaught-exception wrapper. 

(define make-thread 
  (let ((make-cond-wrapper (lambda (thunk lcond lmutex scond smutex)
			     (lambda () 
			       (lock-mutex lmutex)
			       (signal-condition-variable lcond)
			       (lock-mutex smutex)
			       (unlock-mutex lmutex)
			       (wait-condition-variable scond smutex)
			       (unlock-mutex smutex)
			       (with-exception-handler initial-handler 
						       thunk)))))
    (lambda (thunk . name)
      (let ((n (and (pair? name) (car name)))

	    (lm (make-mutex 'launch-mutex))
	    (lc (make-condition-variable 'launch-condition-variable))
	    (sm (make-mutex 'start-mutex))
	    (sc (make-condition-variable 'start-condition-variable)))
	
	(lock-mutex lm)
	(let ((t (call-with-new-thread (make-cond-wrapper thunk lc lm sc sm)
				       srfi-18-exception-handler)))
	  (hashq-set! thread-start-conds t (cons sm sc))
	  (and n (hashq-set! object-names t n))
	  (wait-condition-variable lc lm)
	  (unlock-mutex lm)
	  t)))))

(define (thread-name thread)
  (hashq-ref object-names (check-arg-type thread? thread "thread-name")))

(define (thread-specific thread)
  (hashq-ref object-specifics 
	     (check-arg-type thread? thread "thread-specific")))

(define (thread-specific-set! thread obj)
  (hashq-set! object-specifics
	      (check-arg-type thread? thread "thread-specific-set!")
	      obj)
  *unspecified*)

(define (thread-start! thread)
  (let ((x (hashq-ref thread-start-conds
		      (check-arg-type thread? thread "thread-start!"))))
    (and x (let ((smutex (car x))
		 (scond (cdr x)))
	     (hashq-remove! thread-start-conds thread)
	     (lock-mutex smutex)
	     (signal-condition-variable scond)
	     (unlock-mutex smutex)))
    thread))

(define (thread-yield!) (yield) *unspecified*)

(define (thread-sleep! timeout)
  (let* ((ct (time->seconds (current-time)))
	 (t (cond ((time? timeout) (- (time->seconds timeout) ct))
		  ((number? timeout) (- timeout ct))
		  (else (scm-error 'wrong-type-arg caller
				   "Wrong type argument: ~S" 
				   (list timeout) 
				   '()))))
	 (secs (inexact->exact (truncate t)))
	 (usecs (inexact->exact (truncate (* (- t secs) 1000)))))
    (and (> secs 0) (sleep secs))
    (and (> usecs 0) (usleep usecs))
    *unspecified*))

;; A convenience function for installing exception handlers on SRFI-18 
;; primitives that resume the calling continuation after the handler is 
;; invoked -- this resolves a behavioral incompatibility with Guile's
;; implementation of SRFI-34, which uses lazy-catch and rethrows handled
;; exceptions.  (SRFI-18, "Primitives and exceptions")

(define (wrap thunk)
  (lambda (continuation)
    (with-exception-handler (lambda (obj)
			      (apply (current-exception-handler) (list obj))
			      (apply continuation (list)))
			    thunk)))

;; A pass-thru to cancel-thread that first installs a handler that throws
;; terminated-thread exception, as per SRFI-18, 

(define (thread-terminate! thread)
  (define (thread-terminate-inner!)
    (let ((current-handler (thread-cleanup thread)))
      (if (thunk? current-handler)
	  (set-thread-cleanup! thread 
			       (lambda ()
				 (with-exception-handler initial-handler
							 current-handler) 
				 (srfi-18-exception-preserver
				  terminated-thread-exception)))
	  (set-thread-cleanup! thread
			       (lambda () (srfi-18-exception-preserver
					   terminated-thread-exception))))
      (cancel-thread thread)
      *unspecified*))
  (thread-terminate-inner!))

(define (thread-join! thread . args) 
  (define thread-join-inner!
    (wrap (lambda ()
	    (let ((v (apply join-thread (cons thread args)))
		  (e (thread->exception thread)))
	      (if (and (= (length args) 1) (not v))
		  (raise join-timeout-exception))
	      (if e (raise e))
	      v))))
  (call/cc thread-join-inner!))

;; MUTEXES
;; These functions are all pass-thrus to the existing Guile implementations.

(define make-mutex
  (lambda name
    (let ((n (and (pair? name) (car name)))
	  (m ((@ (guile) make-mutex) 
	      'unchecked-unlock 
	      'allow-external-unlock 
	      'recursive)))
      (and n (hashq-set! object-names m n)) m)))

(define (mutex-name mutex)
  (hashq-ref object-names (check-arg-type mutex? mutex "mutex-name")))

(define (mutex-specific mutex)
  (hashq-ref object-specifics 
	     (check-arg-type mutex? mutex "mutex-specific")))

(define (mutex-specific-set! mutex obj)
  (hashq-set! object-specifics
	      (check-arg-type mutex? mutex "mutex-specific-set!")
	      obj)
  *unspecified*)

(define (mutex-state mutex)
  (let ((owner (mutex-owner mutex)))
    (if owner
	(if (thread-exited? owner) 'abandoned owner)
	(if (> (mutex-level mutex) 0) 'not-owned 'not-abandoned))))

(define (mutex-lock! mutex . args) 
  (define mutex-lock-inner!
    (wrap (lambda ()
	    (catch 'abandoned-mutex-error
		   (lambda () (apply lock-mutex (cons mutex args)))
		   (lambda (key . args) (raise abandoned-mutex-exception))))))
  (call/cc mutex-lock-inner!))

(define (mutex-unlock! mutex . args) 
  (apply unlock-mutex (cons mutex args)))

;; CONDITION VARIABLES
;; These functions are all pass-thrus to the existing Guile implementations.

(define make-condition-variable
  (lambda name
    (let ((n (and (pair? name) (car name)))
	  (m ((@ (guile) make-condition-variable))))
      (and n (hashq-set! object-names m n)) m)))

(define (condition-variable-name condition-variable)
  (hashq-ref object-names (check-arg-type condition-variable? 
					  condition-variable
					  "condition-variable-name")))

(define (condition-variable-specific condition-variable)
  (hashq-ref object-specifics (check-arg-type condition-variable? 
					      condition-variable 
					      "condition-variable-specific")))

(define (condition-variable-specific-set! condition-variable obj)
  (hashq-set! object-specifics
	      (check-arg-type condition-variable? 
			      condition-variable 
			      "condition-variable-specific-set!")
	      obj)
  *unspecified*)

(define (condition-variable-signal! cond) 
  (signal-condition-variable cond) 
  *unspecified*)

(define (condition-variable-broadcast! cond)
  (broadcast-condition-variable cond)
  *unspecified*)

;; TIME

(define current-time gettimeofday)
(define (time? obj)
  (and (pair? obj)
       (let ((co (car obj))) (and (integer? co) (>= co 0)))
       (let ((co (cdr obj))) (and (integer? co) (>= co 0)))))

(define (time->seconds time) 
  (and (check-arg-type time? time "time->seconds")
       (+ (car time) (/ (cdr time) 1000000))))

(define (seconds->time x)
  (and (check-arg-type number? x "seconds->time")
       (let ((fx (truncate x)))
	 (cons (inexact->exact fx)
	       (inexact->exact (truncate (* (- x fx) 1000000)))))))

;; srfi-18.scm ends here

[-- Attachment #5: srfi-18.test --]
[-- Type: application/octet-stream, Size: 15868 bytes --]

;;;; srfi-18.test --- Test suite for Guile's SRFI-18 functions. -*- scheme -*-
;;;; Julian Graham, 2007-10-26
;;;;
;;;; Copyright (C) 2007 Free Software Foundation, Inc.
;;;; 
;;;; This program is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation; either version 2, or (at your option)
;;;; any later version.
;;;; 
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;; GNU General Public License for more details.
;;;; 
;;;; You should have received a copy of the GNU General Public License
;;;; along with this software; see the file COPYING.  If not, write to
;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;;;; Boston, MA 02110-1301 USA

(define-module (test-suite test-srfi-18)
  #:use-module (test-suite lib)
  #:use-module (srfi srfi-18))

(with-test-prefix "current-thread"

  (pass-if "current-thread eq current-thread"
    (eq? (current-thread) (current-thread))))

(with-test-prefix "thread?"

  (pass-if "current-thread is thread"
    (thread? (current-thread)))

  (pass-if "foo not thread"
    (not (thread? 'foo))))

(with-test-prefix "make-thread"

  (pass-if "make-thread creates new thread"
    (let* ((n (length (all-threads)))
	   (t (make-thread (lambda () 'foo) 'make-thread-1))
	   (r (> (length (all-threads)) n)))
      (thread-terminate! t) r)))

(with-test-prefix "thread-name"

  (pass-if "make-thread with name binds name"
    (let* ((t (make-thread (lambda () 'foo) 'thread-name-1))
	   (r (eq? (thread-name t) 'thread-name-1)))
      (thread-terminate! t) r))

  (pass-if "make-thread without name does not bind name"
    (let* ((t (make-thread (lambda () 'foo)))
	   (r (not (thread-name t))))
      (thread-terminate! t) r)))

(with-test-prefix "thread-specific"

  (pass-if "thread-specific is initially #f"
    (let* ((t (make-thread (lambda () 'foo) 'thread-specific-1))
	   (r (not (thread-specific t))))
      (thread-terminate! t) r))

  (pass-if "thread-specific-set! can set value"
    (let ((t (make-thread (lambda () 'foo) 'thread-specific-2)))
      (thread-specific-set! t "hello")
      (let ((r (equal? (thread-specific t) "hello")))
	(thread-terminate! t) r))))

(with-test-prefix "thread-start!"

  (pass-if "thread activates only after start" 
    (let* ((started #f)
	   (m (make-mutex 'thread-start-mutex))
	   (t (make-thread (lambda () (set! started #t)) 'thread-start-1)))
      (and (not started) (thread-start! t) (thread-join! t) started))))

(with-test-prefix "thread-yield!"

  (pass-if "thread yield suceeds"
    (thread-yield!) #t))

(with-test-prefix "thread-sleep!"

  (pass-if "thread sleep with time"
    (let ((future-time (seconds->time (+ (time->seconds (current-time)) 2))))
      (unspecified? (thread-sleep! future-time))))

  (pass-if "thread sleep with number"
    (let ((old-secs (car (current-time))))
      (unspecified? (thread-sleep! (+ (time->seconds (current-time)))))))

  (pass-if "thread does not sleep on past time"
    (let ((past-time (seconds->time (- (time->seconds (current-time)) 2))))
      (unspecified? (thread-sleep! past-time)))))

(with-test-prefix "thread-terminate!"
  
  (pass-if "termination destroys non-started thread"
    (let ((t (make-thread (lambda () 'nothing) 'thread-terminate-1))
	  (num-threads (length (all-threads)))
	  (success #f))
      (thread-terminate! t)
      (with-exception-handler 
       (lambda (obj) (set! success (terminated-thread-exception? obj)))
       (lambda () (thread-join! t)))
      success))

  (pass-if "termination destroys started thread"
    (let* ((m1 (make-mutex 'thread-terminate-2a))
	   (m2 (make-mutex 'thread-terminate-2b))
	   (c (make-condition-variable 'thread-terminate-2))
	   (t (make-thread (lambda () 
			     (mutex-lock! m1) 
			     (condition-variable-signal! c)
			     (mutex-unlock! m1)
			     (mutex-lock! m2))
			   'thread-terminate-2))
	   (success #f))
      (mutex-lock! m1)
      (mutex-lock! m2)
      (thread-start! t)
      (mutex-unlock! m1 c)
      (thread-terminate! t)
      (with-exception-handler
       (lambda (obj) (set! success (terminated-thread-exception? obj)))
       (lambda () (thread-join! t)))
      success)))

(with-test-prefix "thread-join!"

  (pass-if "join receives result of thread"
    (let ((t (make-thread (lambda () 'foo) 'thread-join-1)))
      (thread-start! t)
      (eq? (thread-join! t) 'foo)))

  (pass-if "join receives timeout val if timeout expires"
    (let* ((m (make-mutex 'thread-join-2))
	   (t (make-thread (lambda () (mutex-lock! m)) 'thread-join-2)))
      (mutex-lock! m)
      (thread-start! t)
      (let ((r (thread-join! t (current-time) 'bar)))
	(thread-terminate! t)
	(eq? r 'bar))))

  (pass-if "join throws exception on timeout without timeout val"
    (let* ((m (make-mutex 'thread-join-3))
	   (t (make-thread (lambda () (mutex-lock! m)) 'thread-join-3))
	   (success #f))
      (mutex-lock! m)
      (thread-start! t)
      (with-exception-handler
       (lambda (obj) (set! success (join-timeout-exception? obj)))
       (lambda () (thread-join! t (current-time))))
      (thread-terminate! t)
      success))

  (pass-if "join waits on timeout"
    (let ((t (make-thread (lambda () (sleep 1) 'foo) 'thread-join-4)))
      (thread-start! t)
      (eq? (thread-join! t (+ (time->seconds (current-time)) 2)) 'foo))))

(with-test-prefix "mutex?"

  (pass-if "make-mutex creates mutex"
    (mutex? (make-mutex)))

  (pass-if "symbol not mutex"
    (not (mutex? 'foo))))

(with-test-prefix "mutex-name"

  (pass-if "make-mutex with name binds name"
    (let* ((m (make-mutex 'mutex-name-1)))
      (eq? (mutex-name m) 'mutex-name-1)))

  (pass-if "make-mutex without name does not bind name"
    (let* ((m (make-mutex)))
      (not (mutex-name m)))))

(with-test-prefix "mutex-specific"

  (pass-if "mutex-specific is initially #f"
    (let ((m (make-mutex 'mutex-specific-1)))
      (not (mutex-specific m))))

  (pass-if "mutex-specific-set! can set value"
    (let ((m (make-mutex 'mutex-specific-2)))
      (mutex-specific-set! m "hello")
      (equal? (mutex-specific m) "hello"))))

(with-test-prefix "mutex-state"

  (pass-if "mutex state is initially not-abandoned"
    (let ((m (make-mutex 'mutex-state-1)))
      (eq? (mutex-state m) 'not-abandoned)))

  (pass-if "mutex state of locked, owned mutex is owner thread"
    (let ((m (make-mutex 'mutex-state-2)))
      (mutex-lock! m)
      (eq? (mutex-state m) (current-thread))))
	  
  (pass-if "mutex state of locked, unowned mutex is not-owned"
    (let ((m (make-mutex 'mutex-state-3)))
      (mutex-lock! m #f #f)
      (eq? (mutex-state m) 'not-owned)))

  (pass-if "mutex state of unlocked, abandoned mutex is abandoned"
    (let* ((m (make-mutex 'mutex-state-4))
	   (t (make-thread (lambda () (mutex-lock! m)))))
      (thread-start! t)
      (thread-join! t)
      (eq? (mutex-state m) 'abandoned))))

(with-test-prefix "mutex-lock!"
  
  (pass-if "mutex-lock! returns true on successful lock"
    (let* ((m (make-mutex 'mutex-lock-1)))
      (mutex-lock! m)))

  (pass-if "mutex-lock! returns false on timeout"
    (let* ((m (make-mutex 'mutex-lock-2))
	   (t (make-thread (lambda () (mutex-lock! m (current-time) #f)))))
      (mutex-lock! m)
      (thread-start! t)
      (not (thread-join! t))))

  (pass-if "mutex-lock! returns true when lock obtained within timeout"
    (let* ((m (make-mutex 'mutex-lock-3))
	   (t (make-thread (lambda () 
			     (mutex-lock! m (+ (time->seconds (current-time)) 
					       100)
					  #f)))))
      (mutex-lock! m)
      (thread-start! t)
      (mutex-unlock! m)
      (thread-join! t)))

  (pass-if "can lock mutex for non-current thread"
    (let* ((m1 (make-mutex 'mutex-lock-4a))
	   (m2 (make-mutex 'mutex-lock-4b))
	   (t (make-thread (lambda () (mutex-lock! m1)) 'mutex-lock-4)))
      (mutex-lock! m1)
      (thread-start! t)
      (mutex-lock! m2 #f t)
      (let ((success (eq? (mutex-state m2) t))) 
	(thread-terminate! t) success)))

  (pass-if "locking abandoned mutex throws exception"
    (let* ((m (make-mutex 'mutex-lock-5))
	   (t (make-thread (lambda () (mutex-lock! m)) 'mutex-lock-5))
	   (success #f))
      (thread-start! t)
      (thread-join! t)
      (with-exception-handler
       (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
       (lambda () (mutex-lock! m)))
      (and success (eq? (mutex-state m) (current-thread)))))

  (pass-if "sleeping threads notified of abandonment"
    (let* ((m1 (make-mutex 'mutex-lock-6a))
	   (m2 (make-mutex 'mutex-lock-6b))
	   (c (make-condition-variable 'mutex-lock-6))
	   (t (make-thread (lambda () 
			     (mutex-lock! m1)
			     (mutex-lock! m2)
			     (condition-variable-signal! c))))
	   (success #f))
      (mutex-lock! m1)
      (thread-start! t)
      (with-exception-handler
       (lambda (obj) (set! success (abandoned-mutex-exception? obj)))
       (lambda () (mutex-unlock! m1 c) (mutex-lock! m2)))
      success)))

(with-test-prefix "mutex-unlock!"
   
  (pass-if "unlock changes mutex state"
    (let* ((m (make-mutex 'mutex-unlock-1)))
      (mutex-lock! m)
      (mutex-unlock! m)
      (eq? (mutex-state m) 'not-abandoned)))

  (pass-if "can unlock from any thread"
    (let* ((m (make-mutex 'mutex-unlock-2))
	   (t (make-thread (lambda () (mutex-unlock! m)) 'mutex-unlock-2)))
      (mutex-lock! m)
      (thread-start! t)
      (thread-join! t)
      (eq? (mutex-state m) 'not-abandoned)))

  (pass-if "mutex unlock is true when condition is signalled"
    (let* ((m (make-mutex 'mutex-unlock-3))
	   (c (make-condition-variable 'mutex-unlock-3))
	   (t (make-thread (lambda () 
			     (mutex-lock! m) 
			     (condition-variable-signal! c) 
			     (mutex-unlock! m)))))
      (mutex-lock! m)
      (thread-start! t)
      (mutex-unlock! m c)))

  (pass-if "mutex unlock is false when condition times out"
    (let* ((m (make-mutex 'mutex-unlock-4))
	   (c (make-condition-variable 'mutex-unlock-4)))
      (mutex-lock! m)
      (not (mutex-unlock! m c (+ (time->seconds (current-time)) 1))))))

(with-test-prefix "condition-variable?"

  (pass-if "make-condition-variable creates condition variable"
    (condition-variable? (make-condition-variable)))

  (pass-if "symbol not condition variable"
    (not (condition-variable? 'foo))))

(with-test-prefix "condition-variable-name"

  (pass-if "make-condition-variable with name binds name"
    (let* ((c (make-condition-variable 'condition-variable-name-1)))
      (eq? (condition-variable-name c) 'condition-variable-name-1)))

  (pass-if "make-condition-variable without name does not bind name"
    (let* ((c (make-condition-variable)))
      (not (condition-variable-name c)))))

(with-test-prefix "condition-variable-specific"

  (pass-if "condition-variable-specific is initially #f"
    (let ((c (make-condition-variable 'condition-variable-specific-1)))
      (not (condition-variable-specific c))))

  (pass-if "condition-variable-specific-set! can set value"
    (let ((c (make-condition-variable 'condition-variable-specific-1)))
      (condition-variable-specific-set! c "hello")
      (equal? (condition-variable-specific c) "hello"))))

(with-test-prefix "condition-variable-signal!"
  
  (pass-if "condition-variable-signal! wakes up single thread"
    (let* ((m (make-mutex 'condition-variable-signal-1))
	   (c (make-condition-variable 'condition-variable-signal-1))
	   (t (make-thread (lambda () 
			     (mutex-lock! m) 
			     (condition-variable-signal! c) 
			     (mutex-unlock! m)))))
      (mutex-lock! m)
      (thread-start! t)
      (mutex-unlock! m c))))

(with-test-prefix "condition-variable-broadcast!"

  (pass-if "condition-variable-broadcast! wakes up multiple threads"
    (let* ((sem 0)
	   (c1 (make-condition-variable 'condition-variable-broadcast-1-a))
	   (m1 (make-mutex 'condition-variable-broadcast-1-a))
	   (c2 (make-condition-variable 'condition-variable-broadcast-1-b))
	   (m2 (make-mutex 'condition-variable-broadcast-1-b))
	   (inc-sem! (lambda () 
		       (mutex-lock! m1)
		       (set! sem (+ sem 1))
		       (condition-variable-broadcast! c1)
		       (mutex-unlock! m1)))
	   (dec-sem! (lambda ()
		       (mutex-lock! m1)
		       (while (eqv? sem 0) (wait-condition-variable c1 m1))
		       (set! sem (- sem 1))
		       (mutex-unlock! m1)))
	   (t1 (make-thread (lambda () 
			      (mutex-lock! m2)
			      (inc-sem!)
			      (mutex-unlock! m2 c2)
			      (inc-sem!))))
	   (t2 (make-thread (lambda () 
			      (mutex-lock! m2)
			      (inc-sem!)
			      (mutex-unlock! m2 c2)
			      (inc-sem!)))))
      (thread-start! t1)
      (thread-start! t2)
      (dec-sem!)
      (dec-sem!)
      (mutex-lock! m2)
      (condition-variable-broadcast! c2)
      (mutex-unlock! m2)
      (dec-sem!)
      (dec-sem!))))

(with-test-prefix "time?"

  (pass-if "current-time is time" (time? (current-time)))
  (pass-if "number is not time" (not (time? 123)))
  (pass-if "symbol not time" (not (time? 'foo))))

(with-test-prefix "time->seconds"

  (pass-if "time->seconds makes time into rational"
    (rational? (time->seconds (current-time))))

  (pass-if "time->seconds is reversible"
    (let ((t (current-time)))
      (equal? t (seconds->time (time->seconds t))))))

(with-test-prefix "seconds->time"

  (pass-if "seconds->time makes rational into time"
    (time? (seconds->time 123.456)))

  (pass-if "seconds->time is reversible"
    (let ((t (time->seconds (current-time))))
      (equal? t (time->seconds (seconds->time t))))))

(with-test-prefix "current-exception-handler"

  (pass-if "current handler returned at top level"
    (procedure? (current-exception-handler)))

  (pass-if "specified handler set under with-exception-handler"
    (let ((h (lambda (key . args) 'nothing)))
      (with-exception-handler h (lambda () (eq? (current-exception-handler) 
						h)))))

  (pass-if "multiple levels of handler nesting"
    (let ((h (lambda (key . args) 'nothing))
	  (i (current-exception-handler)))
      (and (with-exception-handler h (lambda () 
				       (eq? (current-exception-handler) h)))
	   (eq? (current-exception-handler) i))))

  (pass-if "exception handler installation is thread-safe"
    (let* ((h1 (current-exception-handler))
	   (h2 (lambda (key . args) 'nothing-2))
	   (m (make-mutex 'current-exception-handler-4))
	   (c (make-condition-variable 'current-exception-handler-4))
	   (t (make-thread (lambda () 
			     (with-exception-handler 
			      h2 (lambda () 
				   (mutex-lock! m) 
				   (condition-variable-signal! c) 
				   (wait-condition-variable c m)
				   (and (eq? (current-exception-handler) h2)
					(mutex-unlock! m)))))
			   'current-exception-handler-4)))
      (mutex-lock! m)
      (thread-start! t)
      (wait-condition-variable c m)
      (and (eq? (current-exception-handler) h1)
	   (condition-variable-signal! c)
	   (mutex-unlock! m)
	   (thread-join! t)))))

(with-test-prefix "uncaught-exception-reason"

  (pass-if "initial handler captures top level exception"
    (let ((t (make-thread (lambda () (raise 'foo))))
	  (success #f))
      (thread-start! t)
      (with-exception-handler
       (lambda (obj)
	 (and (uncaught-exception? obj)
	      (eq? (uncaught-exception-reason obj) 'foo)
	      (set! success #t)))
       (lambda () (thread-join! t)))
      success))

  (pass-if "initial handler captures non-SRFI-18 throw"
    (let ((t (make-thread (lambda () (throw 'foo))))
	  (success #f))
      (thread-start! t)
      (with-exception-handler
       (lambda (obj)
	 (and (uncaught-exception? obj)
	      (eq? (uncaught-exception-reason obj) 'foo)
	      (set! success #t)))
       (lambda () (thread-join! t)))
      success)))

  reply	other threads:[~2008-04-14  0:43 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-11  1:54 srfi-18 requirements Julian Graham
2007-10-12  8:42 ` Ludovic Courtès
2007-10-12 15:31   ` Julian Graham
2007-10-15 22:26     ` Julian Graham
2007-10-15 22:35       ` Stephen Compall
2007-10-15 22:47         ` Julian Graham
2007-10-29 14:37           ` Julian Graham
2007-11-26 18:11             ` Julian Graham
2007-11-27  9:14               ` Ludovic Courtès
2007-11-28 18:23             ` Ludovic Courtès
2007-11-28 18:55               ` Julian Graham
2007-12-01  5:08               ` Julian Graham
2007-12-01 10:21                 ` Ludovic Courtès
2007-12-02  3:59                   ` Julian Graham
2007-12-04 22:20                     ` Neil Jerram
2007-12-04 22:29                 ` Neil Jerram
2007-12-11  4:20                   ` Julian Graham
2007-12-18  4:30               ` Julian Graham
2007-12-28 18:46                 ` Ludovic Courtès
2007-12-28 19:08                   ` Julian Graham
2007-12-28 22:35                     ` Neil Jerram
2007-12-30 11:04                 ` Neil Jerram
2007-12-30 20:38                   ` Julian Graham
2008-01-01 19:09                     ` Neil Jerram
2008-01-04  5:01                       ` Julian Graham
2008-01-05  0:30                         ` Neil Jerram
2008-01-06 21:41                           ` Julian Graham
2008-01-08 23:11                             ` Neil Jerram
2008-01-11  2:39                               ` Julian Graham
2008-01-17  1:48                                 ` Neil Jerram
2008-01-19 20:10                                   ` Julian Graham
2008-01-23 22:46                                     ` Neil Jerram
2008-01-23 23:23                                       ` Julian Graham
2008-01-25  1:07                                         ` Neil Jerram
2008-01-25  1:38                                           ` Julian Graham
2008-01-28  2:06                                             ` Julian Graham
2008-02-03  0:30                                               ` Neil Jerram
2008-02-05  6:27                                                 ` Julian Graham
2008-02-07  1:23                                                   ` Neil Jerram
2008-02-07  3:06                                                     ` Julian Graham
2008-02-07 23:26                                                       ` Neil Jerram
2008-02-07 23:33                                                         ` Julian Graham
2008-02-07 23:38                                                     ` Neil Jerram
2008-02-08  0:04                                                       ` Julian Graham
2008-02-11  5:14                                                         ` Julian Graham
2008-02-19 22:48                                                           ` Neil Jerram
2008-02-20  2:10                                                             ` Julian Graham
2008-02-22  0:33                                                               ` Neil Jerram
2008-02-22  4:14                                                                 ` Julian Graham
2008-02-24  9:41                                                                   ` Neil Jerram
2008-02-24 18:17                                                                     ` Julian Graham
2008-02-24 23:29                                                                       ` Neil Jerram
2008-03-01 19:56                                                                         ` Julian Graham
2008-03-08 16:34                                                                           ` Neil Jerram
2008-03-11  4:02                                                                             ` Julian Graham
2008-03-22 18:55                                                                               ` Julian Graham
2008-03-23 23:57                                                                                 ` Neil Jerram
2008-03-24 22:03                                                                               ` Neil Jerram
2008-03-26 15:55                                                                                 ` Julian Graham
2008-04-03  0:18                                                                                   ` Neil Jerram
2008-04-03 19:07                                                                                     ` Julian Graham
2008-04-09 21:29                                                                                       ` Neil Jerram
2008-04-14  0:43                                                                                         ` Julian Graham [this message]
2008-05-14  1:23                                                                                           ` Julian Graham
2008-05-14 21:13                                                                                             ` Neil Jerram
2008-05-14 23:11                                                                                           ` Neil Jerram
2008-05-15  5:05                                                                                             ` Julian Graham
2008-05-24 11:42                                                                                               ` Neil Jerram
2008-05-24 13:55                                                                                                 ` Neil Jerram
2008-05-25  2:07                                                                                                 ` Julian Graham
2008-05-31 21:41                                                                                                 ` Ludovic Courtès
2008-06-02  4:48                                                                                                   ` Julian Graham
2008-06-21  5:03                                                                                                     ` Julian Graham
2008-06-30 17:51                                                                                                       ` Ludovic Courtès
2008-01-08 23:41                             ` Neil Jerram

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2bc5f8210804131743p10e3a24bu15a4fb1985f72d1b@mail.gmail.com \
    --to=joolean@gmail.com \
    --cc=guile-devel@gnu.org \
    --cc=ludo@gnu.org \
    --cc=neil@ossau.uklinux.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).