Read a unique thread identifier from CPU registers

This is, depending on the implementation of pthread, significantly
cheaper than using a pthread library call.
Even if we don't know the assembler for an architecture, taking
the address of the thread_local variable is still faster.

As QThread::currentThreadId() is documented to be used internally
and not meant for application code, we don't have to care about
what exact value we return. Internally, we use it only to compare
thread IDs for equality, which this implementation is sufficient
for, even if a thread ID is re-used when one of the threads
terminate and a new thread starts (since the other thread is still
executing code). Besides, pthread_self documents [0] that a thread
ID may be reused, and that the returned pthread_t cannot be
portably compared using operator==(); using pthread_equal would
require adding a Qt thread-ID type that implements this correctly,
and would make things even slower.

[0] http://man7.org/linux/man-pages/man3/pthread_self.3.html

Change-Id: Id08e79b9b9c88976561f7cd36c66d43771fc4f24
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Volker Hilsheimer 2019-08-19 13:33:31 +02:00
parent ab5153aa0b
commit 5e9b2ade67
4 changed files with 59 additions and 6 deletions

View File

@ -844,7 +844,7 @@ bool QThread::event(QEvent* event)
return QObject::event(event);
}
Qt::HANDLE QThread::currentThreadId() noexcept
Qt::HANDLE QThread::currentThreadIdImpl() noexcept
{
return Qt::HANDLE(currentThread());
}

View File

@ -161,6 +161,7 @@ private:
#if QT_CONFIG(cxx11_future)
static QThread *createThreadImpl(std::future<void> &&future);
#endif
static Qt::HANDLE currentThreadIdImpl() noexcept Q_DECL_PURE_FUNCTION;
friend class QCoreApplication;
friend class QThreadData;
@ -236,6 +237,38 @@ QThread *QThread::create(Function &&f)
#endif // QT_CONFIG(cxx11_future)
/*
On architectures and platforms we know, interpret the thread control
block (TCB) as a unique identifier for a thread within a process. Otherwise,
fall back to a slower but safe implementation.
As per the documentation of currentThreadId, we return an opaque handle
as a thread identifier, and application code is not supposed to use that
value for anything. In Qt we use the handle to check if threads are identical,
for which the TCB is sufficient.
So we use the fastest possible way, rathern than spend time on returning
some pseudo-interoperable value.
*/
inline Qt::HANDLE QThread::currentThreadId() noexcept
{
Qt::HANDLE tid; // typedef to void*
Q_STATIC_ASSERT(sizeof(tid) == sizeof(void*));
// See https://akkadia.org/drepper/tls.pdf for x86 ABI
#if defined(Q_PROCESSOR_X86_32) && defined(Q_OS_LINUX) // x86 32-bit always uses GS
__asm__("movl %%gs:0, %0" : "=r" (tid) : : );
#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_DARWIN64)
// 64bit macOS uses GS, see https://github.com/apple/darwin-xnu/blob/master/libsyscall/os/tsd.h
__asm__("movq %%gs:0, %0" : "=r" (tid) : : );
#elif defined(Q_PROCESSOR_X86_64) && (defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD))
// x86_64 Linux, BSD uses FS
__asm__("movq %%fs:0, %0" : "=r" (tid) : : );
#else
tid = currentThreadIdImpl();
#endif
return tid;
}
QT_END_NAMESPACE
#endif // QTHREAD_H

View File

@ -415,16 +415,36 @@ void QThreadPrivate::finish(void *arg)
}
/**************************************************************************
** QThread
*************************************************************************/
Qt::HANDLE QThread::currentThreadId() noexcept
/*
Since each thread is guaranteed to have its own copy of
currenThreadData, the address is guaranteed to be unique for each
running thread (but likely to be reused for newly started threads).
CI tests fails on ARM architectures if we try to use the assembler,
or the address of the thread_local (even with a recent gcc version), so
stick to the pthread version there. The assembler would be
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/Babeihid.html
asm volatile ("mrc p15, 0, %0, c13, c0, 3" : "=r" (tid));
and
// see glibc/sysdeps/aarch64/nptl/tls.h
asm volatile ("mrs %0, tpidr_el0" : "=r" (tid));
for 32 and 64bit versions, respectively.
*/
Qt::HANDLE QThread::currentThreadIdImpl() noexcept
{
// requires a C cast here otherwise we run into trouble on AIX
#if defined(Q_PROCESSOR_ARM)
return to_HANDLE(pthread_self());
#else
return &currentThreadData;
#endif
}
#if defined(QT_LINUXBASE) && !defined(_SC_NPROCESSORS_ONLN)

View File

@ -449,7 +449,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
** QThread
*************************************************************************/
Qt::HANDLE QThread::currentThreadId() noexcept
Qt::HANDLE QThread::currentThreadIdImpl() noexcept
{
return reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId()));
}