QThread/Unix: use pthread_timedjoin() support if it's not worse

For some legacy reason at the time they were proposed, this extension
function is defined to operate on CLOCK_REALTIME, which is subject to
time jumps. I believe the proposal to POSIX is to implement the
clockjoin() function instead.

This commit implements support for using it if it won't make the
situation worse: that is, if QWaitCondition is already using the real-
time clock. This will enable support for Apple Darwin systems and for
QNX. The other BSDs are left out because they do have CLOCK_MONOTONIC
and pthread_condattr_setclock().

Change-Id: Ib97a7f1750cb4e3da5d1fffd68efdec02615b9ec
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2024-11-02 10:28:12 -07:00
parent db880ea6b2
commit 5d1eba05e5
2 changed files with 42 additions and 11 deletions

View File

@ -446,6 +446,23 @@ int main()
}
")
# pthread_timedjoin
qt_config_compile_test(pthread_timedjoin
LABEL "pthread_timedjoin()"
LIBRARIES Threads::Threads
CODE
"#include <pthread.h>
#if __has_include(<pthread_np.h>)
# include <pthread_np.h>
#endif
int main()
{
void *ret;
const struct timespec ts = {};
return pthread_timedjoin_np(pthread_self(), &ret, &ts);
}
")
# renameat2
qt_config_compile_test(renameat2
LABEL "renameat2()"
@ -713,6 +730,11 @@ qt_feature("pthread_clockjoin" PRIVATE
AUTODETECT UNIX
CONDITION UNIX AND QT_FEATURE_thread AND TEST_pthread_clockjoin
)
qt_feature("pthread_timedjoin" PRIVATE
LABEL "pthread_timedjoin() function"
AUTODETECT UNIX
CONDITION UNIX AND QT_FEATURE_thread AND TEST_pthread_timedjoin
)
qt_feature("qqnx_pps" PRIVATE
LABEL "PPS"
CONDITION PPS_FOUND

View File

@ -28,6 +28,9 @@
#include <sched.h>
#include <errno.h>
#if __has_include(<pthread_np.h>)
# include <pthread_np.h>
#endif
#if defined(Q_OS_FREEBSD)
# include <sys/cpuset.h>
@ -73,10 +76,10 @@ static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
enum { ThreadPriorityResetFlag = 0x80000000 };
#if QT_CONFIG(pthread_clockjoin)
// If we have a way to perform a timed pthread_join(), we will do it. This
// ensures that QThread::wait() only returns after pthread_join() or equivalent
// has returned, ensuring that the thread has definitely exited.
// If we have a way to perform a timed pthread_join(), we will do it if its
// clock is not worse than the one QWaitCondition is using. This ensures that
// QThread::wait() only returns after pthread_join() or equivalent has
// returned, ensuring that the thread has definitely exited.
//
// Because only one thread can call this family of functions at a time, we
// count how many threads are waiting and all but one of them wait on a
@ -85,17 +88,20 @@ enum { ThreadPriorityResetFlag = 0x80000000 };
// thread in charge wakes up one of the other waiters (if there's any) to
// assume responsibility for joining.
//
// We don't bother with pthread_timedjoin() implementations that take a
// CLOCK_REALTIME timeout.
#else
// If we don't have a way to perform timed pthread_join(), then we don't try
// joining a all. All waiting threads will wait for the launched thread to
// call QWaitCondition::wakeAll(). Note in this case it is possible for the
// waiting threads to conclude the launched thread has exited before it has.
//
// To support this scenario, we start the thread in detached state.
static constexpr bool UsingPThreadTimedJoin = QT_CONFIG(pthread_clockjoin)
|| (QT_CONFIG(pthread_timedjoin) && SteadyClockClockId == CLOCK_REALTIME);
#if !QT_CONFIG(pthread_clockjoin)
int pthread_clockjoin_np(...) { return ENOSYS; } // pretend
#endif
#if !QT_CONFIG(pthread_timedjoin)
int pthread_timedjoin_np(...) { return ENOSYS; } // pretend
#endif
#if QT_CONFIG(broken_threadlocal_dtors)
// On most modern platforms, the C runtime has a helper function that helps the
@ -707,7 +713,7 @@ void QThread::start(Priority priority)
pthread_attr_t attr;
pthread_attr_init(&attr);
if constexpr (!QT_CONFIG(pthread_clockjoin))
if constexpr (!UsingPThreadTimedJoin)
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#ifdef Q_OS_DARWIN
if (d->serviceLevel != QThread::QualityOfService::Auto)
@ -852,7 +858,7 @@ static void wakeAllInternal(QThreadPrivate *d)
inline void QThreadPrivate::wakeAll()
{
if (data->isAdopted || !QT_CONFIG(pthread_clockjoin))
if (data->isAdopted || !UsingPThreadTimedJoin)
wakeAllInternal(this);
}
@ -908,7 +914,10 @@ bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
pthread_cleanup_push(&CancelState::run, &nocancel);
pthread_t thrId = from_HANDLE<pthread_t>(data->threadId.loadRelaxed());
r = pthread_clockjoin_np(thrId, nullptr, SteadyClockClockId, pts);
if constexpr (QT_CONFIG(pthread_clockjoin))
r = pthread_clockjoin_np(thrId, nullptr, SteadyClockClockId, pts);
else
r = pthread_timedjoin_np(thrId, nullptr, pts);
Q_ASSERT(r == 0 || r == ETIMEDOUT);
pthread_cleanup_pop(1);
@ -927,7 +936,7 @@ bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
--(*static_cast<decltype(waiters) *>(ptr));
}, &waiters);
for (;;) {
if (QT_CONFIG(pthread_clockjoin) && mustJoin && !data->isAdopted) {
if (UsingPThreadTimedJoin && mustJoin && !data->isAdopted) {
result = doJoin();
break;
}