From c05293d7d166e7ab1fc20e9eff16ee8a290475d9 Mon Sep 17 00:00:00 2001 From: markj Date: Fri, 8 Mar 2019 21:07:08 +0000 Subject: [PATCH] Have pthread_cond_destroy() return EBUSY if the condvar has waiters. This is not required of a compliant implementation, but it's easy to check for and helps improve compatibility with other common implementations. Moreover, it's consistent with our pthread_mutex_destroy(). PR: 234805 Reviewed by: jhb, kib, ngie MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D19496 --- contrib/netbsd-tests/lib/libpthread/t_cond.c | 92 ++++++++++++++++++++ lib/libthr/thread/thr_cond.c | 19 ++-- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/contrib/netbsd-tests/lib/libpthread/t_cond.c b/contrib/netbsd-tests/lib/libpthread/t_cond.c index 83a3833015d..9f51a380ef0 100644 --- a/contrib/netbsd-tests/lib/libpthread/t_cond.c +++ b/contrib/netbsd-tests/lib/libpthread/t_cond.c @@ -493,6 +493,51 @@ ATF_TC_BODY(bogus_timedwaits, tc) PTHREAD_REQUIRE(pthread_mutex_unlock(&static_mutex)); } +#ifdef __FreeBSD__ +static void * +destroy_busy_threadfunc(void *arg) +{ + PTHREAD_REQUIRE(pthread_mutex_lock(&mutex)); + + share = 1; + PTHREAD_REQUIRE(pthread_cond_broadcast(&cond)); + PTHREAD_REQUIRE(pthread_cond_wait(&cond, &mutex)); + + PTHREAD_REQUIRE(pthread_mutex_unlock(&mutex)); + + return NULL; +} + +ATF_TC(destroy_busy); +ATF_TC_HEAD(destroy_busy, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks non-standard behaviour of " + "returning EBUSY when attempting to destroy an active condvar"); +} +ATF_TC_BODY(destroy_busy, tc) +{ + pthread_t thread; + + PTHREAD_REQUIRE(pthread_mutex_init(&mutex, NULL)); + PTHREAD_REQUIRE(pthread_cond_init(&cond, NULL)); + PTHREAD_REQUIRE(pthread_mutex_lock(&mutex)); + PTHREAD_REQUIRE(pthread_create(&thread, NULL, destroy_busy_threadfunc, + NULL)); + + while (share == 0) { + PTHREAD_REQUIRE(pthread_cond_wait(&cond, &mutex)); + } + + PTHREAD_REQUIRE_STATUS(pthread_cond_destroy(&cond), EBUSY); + PTHREAD_REQUIRE(pthread_cond_signal(&cond)); + PTHREAD_REQUIRE(pthread_cond_destroy(&cond)); + + PTHREAD_REQUIRE(pthread_mutex_unlock(&mutex)); + PTHREAD_REQUIRE(pthread_join(thread, NULL)); + PTHREAD_REQUIRE(pthread_mutex_destroy(&mutex)); +} +#endif + static void unlock(void *arg) { @@ -547,6 +592,49 @@ ATF_TC_BODY(destroy_after_cancel, tc) PTHREAD_REQUIRE(pthread_mutex_destroy(&mutex)); } +static void * +destroy_after_signal_threadfunc(void *arg) +{ + PTHREAD_REQUIRE(pthread_mutex_lock(&mutex)); + + share = 1; + PTHREAD_REQUIRE(pthread_cond_broadcast(&cond)); + PTHREAD_REQUIRE(pthread_cond_wait(&cond, &mutex)); + + PTHREAD_REQUIRE(pthread_mutex_unlock(&mutex)); + + return NULL; +} + +ATF_TC(destroy_after_signal); +ATF_TC_HEAD(destroy_after_signal, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks destroying a condition variable " + "immediately after signaling waiters"); +} +ATF_TC_BODY(destroy_after_signal, tc) +{ + pthread_t thread; + + PTHREAD_REQUIRE(pthread_mutex_init(&mutex, NULL)); + PTHREAD_REQUIRE(pthread_cond_init(&cond, NULL)); + PTHREAD_REQUIRE(pthread_mutex_lock(&mutex)); + PTHREAD_REQUIRE(pthread_create(&thread, NULL, + destroy_after_signal_threadfunc, NULL)); + + while (share == 0) { + PTHREAD_REQUIRE(pthread_cond_wait(&cond, &mutex)); + } + + PTHREAD_REQUIRE(pthread_cond_signal(&cond)); + PTHREAD_REQUIRE(pthread_cond_destroy(&cond)); + PTHREAD_REQUIRE(pthread_mutex_unlock(&mutex)); + + PTHREAD_REQUIRE(pthread_join(thread, NULL)); + + PTHREAD_REQUIRE(pthread_mutex_destroy(&mutex)); +} + ATF_TC(condattr); ATF_TC_HEAD(condattr, tc) { @@ -577,7 +665,11 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, cond_timedwait_race); ATF_TP_ADD_TC(tp, broadcast); ATF_TP_ADD_TC(tp, bogus_timedwaits); +#ifdef __FreeBSD__ + ATF_TP_ADD_TC(tp, destroy_busy); +#endif ATF_TP_ADD_TC(tp, destroy_after_cancel); + ATF_TP_ADD_TC(tp, destroy_after_signal); ATF_TP_ADD_TC(tp, condattr); return atf_no_error(); diff --git a/lib/libthr/thread/thr_cond.c b/lib/libthr/thread/thr_cond.c index ecaf787aa51..35964d1c441 100644 --- a/lib/libthr/thread/thr_cond.c +++ b/lib/libthr/thread/thr_cond.c @@ -166,17 +166,26 @@ _pthread_cond_destroy(pthread_cond_t *cond) error = 0; if (*cond == THR_PSHARED_PTR) { cvp = __thr_pshared_offpage(cond, 0); - if (cvp != NULL) - __thr_pshared_destroy(cond); - *cond = THR_COND_DESTROYED; + if (cvp != NULL) { + if (cvp->kcond.c_has_waiters) + error = EBUSY; + else + __thr_pshared_destroy(cond); + } + if (error == 0) + *cond = THR_COND_DESTROYED; } else if ((cvp = *cond) == THR_COND_INITIALIZER) { /* nothing */ } else if (cvp == THR_COND_DESTROYED) { error = EINVAL; } else { cvp = *cond; - *cond = THR_COND_DESTROYED; - free(cvp); + if (cvp->__has_user_waiters || cvp->kcond.c_has_waiters) + error = EBUSY; + else { + *cond = THR_COND_DESTROYED; + free(cvp); + } } return (error); } -- 2.45.0