diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-06-01 17:27:48 +0000 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-06-01 17:38:31 +0000 |
commit | 8081702460726304af496be52234385094392a6f (patch) | |
tree | 55606e2a42e6342aaed360c27c17e9675c9c478e /sysdeps | |
parent | a3e589d1f68d43d4c3f67d59497862875c2d5afc (diff) | |
download | glibc-8081702460726304af496be52234385094392a6f.zip glibc-8081702460726304af496be52234385094392a6f.tar.gz glibc-8081702460726304af496be52234385094392a6f.tar.bz2 |
htl: Make pthread_cond_destroy wait for threads to be woken
This allows to reuse the storage after calling pthread_cond_destroy.
* sysdeps/htl/bits/types/struct___pthread_cond.h (__pthread_cond):
Replace unused struct __pthread_condimpl *__impl field with unsigned int
__wrefs.
(__PTHREAD_COND_INITIALIZER): Update accordingly.
* sysdeps/htl/pt-cond-timedwait.c (__pthread_cond_timedwait_internal):
Register as waiter in __wrefs field. On unregistering, wake any pending
pthread_cond_destroy.
* sysdeps/htl/pt-cond-destroy.c (__pthread_cond_destroy): Register wake
request in __wrefs.
* nptl/Makefile (tests): Move tst-cond20 tst-cond21 to...
* sysdeps/pthread/Makefile (tests): ... here.
* nptl/tst-cond20.c nptl/tst-cond21.c: Move to...
* sysdeps/pthread/tst-cond20.c sysdeps/pthread/tst-cond21.c: ... here.
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/htl/bits/types/struct___pthread_cond.h | 4 | ||||
-rw-r--r-- | sysdeps/htl/pt-cond-destroy.c | 18 | ||||
-rw-r--r-- | sysdeps/htl/pt-cond-timedwait.c | 11 | ||||
-rw-r--r-- | sysdeps/mach/hurd/htl/pt-hurd-cond-timedwait.c | 11 | ||||
-rw-r--r-- | sysdeps/pthread/Makefile | 2 | ||||
-rw-r--r-- | sysdeps/pthread/tst-cond20.c | 172 | ||||
-rw-r--r-- | sysdeps/pthread/tst-cond21.c | 3 |
7 files changed, 215 insertions, 6 deletions
diff --git a/sysdeps/htl/bits/types/struct___pthread_cond.h b/sysdeps/htl/bits/types/struct___pthread_cond.h index 150a37c..c040b17 100644 --- a/sysdeps/htl/bits/types/struct___pthread_cond.h +++ b/sysdeps/htl/bits/types/struct___pthread_cond.h @@ -27,12 +27,12 @@ struct __pthread_cond __pthread_spinlock_t __lock; struct __pthread *__queue; struct __pthread_condattr *__attr; - struct __pthread_condimpl *__impl; + unsigned int __wrefs; void *__data; }; /* Initializer for a condition variable. */ #define __PTHREAD_COND_INITIALIZER \ - { __PTHREAD_SPIN_LOCK_INITIALIZER, NULL, NULL, NULL, NULL } + { __PTHREAD_SPIN_LOCK_INITIALIZER, NULL, NULL, 0, NULL } #endif /* bits/types/struct___pthread_cond.h */ diff --git a/sysdeps/htl/pt-cond-destroy.c b/sysdeps/htl/pt-cond-destroy.c index 0664f3f..722516a 100644 --- a/sysdeps/htl/pt-cond-destroy.c +++ b/sysdeps/htl/pt-cond-destroy.c @@ -22,14 +22,26 @@ int __pthread_cond_destroy (pthread_cond_t *cond) { - int ret = 0; + /* Set the wake request flag. */ + unsigned int wrefs = atomic_fetch_or_acquire (&cond->__wrefs, 1); __pthread_spin_wait (&cond->__lock); if (cond->__queue) - ret = EBUSY; + { + __pthread_spin_unlock (&cond->__lock); + return EBUSY; + } __pthread_spin_unlock (&cond->__lock); - return ret; + while (wrefs >> 1 != 0) + { + gsync_wait (__mach_task_self (), (vm_offset_t) &cond->__wrefs, wrefs, + 0, 0, 0); + wrefs = atomic_load_acquire (&cond->__wrefs); + } + /* The memory the condvar occupies can now be reused. */ + + return 0; } weak_alias (__pthread_cond_destroy, pthread_cond_destroy); diff --git a/sysdeps/htl/pt-cond-timedwait.c b/sysdeps/htl/pt-cond-timedwait.c index a0ced9a..c05944d 100644 --- a/sysdeps/htl/pt-cond-timedwait.c +++ b/sysdeps/htl/pt-cond-timedwait.c @@ -144,6 +144,10 @@ __pthread_cond_timedwait_internal (pthread_cond_t *cond, /* Release MUTEX before blocking. */ __pthread_mutex_unlock (mutex); + /* Increase the waiter reference count. Relaxed MO is sufficient because + we only need to synchronize when decrementing the reference count. */ + atomic_fetch_add_relaxed (&cond->__wrefs, 2); + /* Block the thread. */ if (abstime != NULL) err = __pthread_timedblock (self, abstime, clock_id); @@ -178,6 +182,13 @@ __pthread_cond_timedwait_internal (pthread_cond_t *cond, } __pthread_spin_unlock (&cond->__lock); + /* If destruction is pending (i.e., the wake-request flag is nonzero) and we + are the last waiter (prior value of __wrefs was 1 << 1), then wake any + threads waiting in pthread_cond_destroy. Release MO to synchronize with + these threads. Don't bother clearing the wake-up request flag. */ + if ((atomic_fetch_add_release (&cond->__wrefs, -2)) == 3) + __gsync_wake (__mach_task_self (), (vm_offset_t) &cond->__wrefs, 0, 0); + if (drain) __pthread_block (self); diff --git a/sysdeps/mach/hurd/htl/pt-hurd-cond-timedwait.c b/sysdeps/mach/hurd/htl/pt-hurd-cond-timedwait.c index 939ed56..4f63955 100644 --- a/sysdeps/mach/hurd/htl/pt-hurd-cond-timedwait.c +++ b/sysdeps/mach/hurd/htl/pt-hurd-cond-timedwait.c @@ -111,6 +111,10 @@ __pthread_hurd_cond_timedwait_internal (pthread_cond_t *cond, /* Release MUTEX before blocking. */ __pthread_mutex_unlock (mutex); + /* Increase the waiter reference count. Relaxed MO is sufficient because + we only need to synchronize when decrementing the reference count. */ + atomic_fetch_add_relaxed (&cond->__wrefs, 2); + /* Block the thread. */ if (abstime != NULL) err = __pthread_timedblock (self, abstime, clock_id); @@ -144,6 +148,13 @@ __pthread_hurd_cond_timedwait_internal (pthread_cond_t *cond, __pthread_block (self); } + /* If destruction is pending (i.e., the wake-request flag is nonzero) and we + are the last waiter (prior value of __wrefs was 1 << 1), then wake any + threads waiting in pthread_cond_destroy. Release MO to synchronize with + these threads. Don't bother clearing the wake-up request flag. */ + if ((atomic_fetch_add_release (&cond->__wrefs, -2)) == 3) + __gsync_wake (__mach_task_self (), (vm_offset_t) &cond->__wrefs, 0, 0); + /* Clear the hook, now that we are done blocking. */ ss->cancel_hook = NULL; /* Check the cancellation flag; we might have unblocked due to diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index 74dbca1..3dc8827 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -49,7 +49,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \ tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond12 tst-cond13 \ tst-cond14 tst-cond15 tst-cond16 tst-cond17 tst-cond18 tst-cond19 \ - tst-cond23 tst-cond24 tst-cond25 tst-cond27 \ + tst-cond20 tst-cond21 tst-cond23 tst-cond24 tst-cond25 tst-cond27 \ tst-cond-except \ tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 tst-join6 tst-join7 \ tst-join8 tst-join9 tst-join10 tst-join11 tst-join12 tst-join13 \ diff --git a/sysdeps/pthread/tst-cond20.c b/sysdeps/pthread/tst-cond20.c new file mode 100644 index 0000000..f4047c0 --- /dev/null +++ b/sysdeps/pthread/tst-cond20.c @@ -0,0 +1,172 @@ +/* Copyright (C) 2004-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2004. + + The GNU C 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. + + The GNU C 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 the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define N 10 +#define ROUNDS 1000 +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; +static pthread_barrier_t b; +static int count; + +static void * +tf (void *p) +{ + int i; + for (i = 0; i < ROUNDS; ++i) + { + pthread_mutex_lock (&mut); + + if (++count == N) + pthread_cond_signal (&cond2); + +#ifdef TIMED + struct timeval tv; + gettimeofday (&tv, NULL); + struct timespec ts; + /* Wait three seconds. */ + ts.tv_sec = tv.tv_sec + 3; + ts.tv_nsec = tv.tv_usec * 1000; + pthread_cond_timedwait (&cond, &mut, &ts); +#else + pthread_cond_wait (&cond, &mut); +#endif + + pthread_mutex_unlock (&mut); + + int err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("child: barrier_wait failed"); + exit (1); + } + + err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("child: barrier_wait failed"); + exit (1); + } + } + + return NULL; +} + + +static int +do_test (void) +{ + if (pthread_barrier_init (&b, NULL, N + 1) != 0) + { + puts ("barrier_init failed"); + return 1; + } + + pthread_mutex_lock (&mut); + + int i, j, err; + pthread_t th[N]; + for (i = 0; i < N; ++i) + if ((err = pthread_create (&th[i], NULL, tf, NULL)) != 0) + { + printf ("cannot create thread %d: %s\n", i, strerror (err)); + return 1; + } + + for (i = 0; i < ROUNDS; ++i) + { + /* Make sure we discard spurious wake-ups. */ + do + pthread_cond_wait (&cond2, &mut); + while (count != N); + + if (i & 1) + pthread_mutex_unlock (&mut); + + if (i & 2) + pthread_cond_broadcast (&cond); + else if (i & 4) + for (j = 0; j < N; ++j) + pthread_cond_signal (&cond); + else + { + for (j = 0; j < (i / 8) % N; ++j) + pthread_cond_signal (&cond); + pthread_cond_broadcast (&cond); + } + + if ((i & 1) == 0) + pthread_mutex_unlock (&mut); + + err = pthread_cond_destroy (&cond); + if (err) + { + printf ("pthread_cond_destroy failed: %s\n", strerror (err)); + return 1; + } + + /* Now clobber the cond variable which has been successfully + destroyed above. */ + memset (&cond, (char) i, sizeof (cond)); + + err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("parent: barrier_wait failed"); + return 1; + } + + pthread_mutex_lock (&mut); + + err = pthread_barrier_wait (&b); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("parent: barrier_wait failed"); + return 1; + } + + count = 0; + err = pthread_cond_init (&cond, NULL); + if (err) + { + printf ("pthread_cond_init failed: %s\n", strerror (err)); + return 1; + } + } + + for (i = 0; i < N; ++i) + if ((err = pthread_join (th[i], NULL)) != 0) + { + printf ("failed to join thread %d: %s\n", i, strerror (err)); + return 1; + } + + puts ("done"); + + return 0; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/sysdeps/pthread/tst-cond21.c b/sysdeps/pthread/tst-cond21.c new file mode 100644 index 0000000..89cb771 --- /dev/null +++ b/sysdeps/pthread/tst-cond21.c @@ -0,0 +1,3 @@ +#include <sys/time.h> +#define TIMED 1 +#include "tst-cond20.c" |