mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
Fix close-port race.
* libguile/ports.c (release_port): Fix race.
This commit is contained in:
parent
dffe495d0d
commit
b392d81c9c
1 changed files with 15 additions and 8 deletions
|
@ -134,15 +134,22 @@ static void
|
||||||
release_port (SCM port)
|
release_port (SCM port)
|
||||||
{
|
{
|
||||||
scm_t_port *pt = SCM_PORT (port);
|
scm_t_port *pt = SCM_PORT (port);
|
||||||
scm_t_uint32 prev;
|
|
||||||
|
|
||||||
prev = scm_atomic_subtract_uint32 (&pt->refcount, 1);
|
/* It's possible for two close-port invocations to race, and since
|
||||||
if (prev == 0)
|
close-port is defined to be idempotent we need to avoid
|
||||||
/* Logic failure. */
|
decrementing the refcount past 0. The normal case is that it's
|
||||||
abort ();
|
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
|
||||||
if (prev > 1)
|
done. However if the current refcount is 0 then the port has been
|
||||||
/* Port still alive. */
|
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;
|
return;
|
||||||
|
|
||||||
/* FIXME: `catch' around the close call? It could throw an exception,
|
/* FIXME: `catch' around the close call? It could throw an exception,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue