guile-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Guile-commits] 07/07: Fix close-port race.


From: Andy Wingo
Subject: [Guile-commits] 07/07: Fix close-port race.
Date: Sun, 8 Jan 2017 14:51:37 +0000 (UTC)

wingo pushed a commit to branch master
in repository guile.

commit b392d81c9ca0bec82b43ca49e8cf96c0e9460a89
Author: Andy Wingo <address@hidden>
Date:   Sun Jan 8 15:42:22 2017 +0100

    Fix close-port race.
    
    * libguile/ports.c (release_port): Fix race.
---
 libguile/ports.c |   23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/libguile/ports.c b/libguile/ports.c
index 20319bc..f415453 100644
--- a/libguile/ports.c
+++ b/libguile/ports.c
@@ -134,15 +134,22 @@ static void
 release_port (SCM port)
 {
   scm_t_port *pt = SCM_PORT (port);
-  scm_t_uint32 prev;
 
-  prev = scm_atomic_subtract_uint32 (&pt->refcount, 1);
-  if (prev == 0)
-    /* Logic failure.  */
-    abort ();
-
-  if (prev > 1)
-    /* Port still alive.  */
+  /* It's possible for two close-port invocations to race, and since
+     close-port is defined to be idempotent we need to avoid
+     decrementing the refcount past 0.  The normal case is that it's
+     open with a refcount of 1 and we're going to change it to 0.
+     Otherwise if the refcount is higher we just subtract 1 and we're
+     done.  However if the current refcount is 0 then the port has been
+     closed or is closing and we just return.  */
+  scm_t_uint32 cur = 1, next = 0;
+  while (!scm_atomic_compare_and_swap_uint32 (&pt->refcount, &cur, next))
+    {
+      if (cur == 0)
+        return;
+      next = cur - 1;
+    }
+  if (cur > 1)
     return;
 
   /* FIXME: `catch' around the close call?  It could throw an exception,



reply via email to

[Prev in Thread] Current Thread [Next in Thread]