1// Copyright (C) 2017 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QFUTEX_P_H
5#define QFUTEX_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qglobal_p.h>
19#include <QtCore/qtsan_impl.h>
20
21QT_BEGIN_NAMESPACE
22
23namespace QtDummyFutex {
24 constexpr inline bool futexAvailable() { return false; }
25 template <typename Atomic>
26 inline bool futexWait(Atomic &, typename Atomic::Type, int = 0)
27 { Q_UNREACHABLE_RETURN(false); }
28 template <typename Atomic> inline void futexWakeOne(Atomic &)
29 { Q_UNREACHABLE(); }
30 template <typename Atomic> inline void futexWakeAll(Atomic &)
31 { Q_UNREACHABLE(); }
32}
33
34QT_END_NAMESPACE
35
36#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
37// use Linux mutexes everywhere except for LSB builds
38# include <sys/syscall.h>
39# include <errno.h>
40# include <limits.h>
41# include <unistd.h>
42# include <asm/unistd.h>
43# include <linux/futex.h>
44# define QT_ALWAYS_USE_FUTEX
45
46// if not defined in linux/futex.h
47# define FUTEX_PRIVATE_FLAG 128 // added in v2.6.22
48
49// RISC-V does not supply __NR_futex
50# ifndef __NR_futex
51# define __NR_futex __NR_futex_time64
52# endif
53
54QT_BEGIN_NAMESPACE
55namespace QtLinuxFutex {
56 constexpr inline bool futexAvailable() { return true; }
57 inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0,
58 int *addr2 = nullptr, int val3 = 0) noexcept
59 {
60 QtTsan::futexRelease(addr, addr2);
61
62 // we use __NR_futex because some libcs (like Android's bionic) don't
63 // provide SYS_futex etc.
64 int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3);
65
66 QtTsan::futexAcquire(addr, addr2);
67
68 return result;
69 }
70 template <typename T> int *addr(T *ptr)
71 {
72 int *int_addr = reinterpret_cast<int *>(ptr);
73#if Q_BYTE_ORDER == Q_BIG_ENDIAN
74 if (sizeof(T) > sizeof(int))
75 int_addr++; //We want a pointer to the least significant half
76#endif
77 return int_addr;
78 }
79
80 template <typename Atomic>
81 inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
82 {
83 _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue));
84 }
85 template <typename Atomic>
86 inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
87 {
88 struct timespec ts;
89 ts.tv_sec = nstimeout / 1000 / 1000 / 1000;
90 ts.tv_nsec = nstimeout % (1000 * 1000 * 1000);
91 int r = _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue), quintptr(&ts));
92 return r == 0 || errno != ETIMEDOUT;
93 }
94 template <typename Atomic> inline void futexWakeOne(Atomic &futex)
95 {
96 _q_futex(addr(&futex), FUTEX_WAKE, 1);
97 }
98 template <typename Atomic> inline void futexWakeAll(Atomic &futex)
99 {
100 _q_futex(addr(&futex), FUTEX_WAKE, INT_MAX);
101 }
102 template <typename Atomic> inline
103 void futexWakeOp(Atomic &futex1, int wake1, int wake2, Atomic &futex2, quint32 op)
104 {
105 _q_futex(addr(&futex1), FUTEX_WAKE_OP, wake1, wake2, addr(&futex2), op);
106 }
107}
108namespace QtFutex = QtLinuxFutex;
109QT_END_NAMESPACE
110
111#elif defined(Q_OS_WIN)
112# include <qt_windows.h>
113
114QT_BEGIN_NAMESPACE
115namespace QtWindowsFutex {
116#define QT_ALWAYS_USE_FUTEX
117constexpr inline bool futexAvailable() { return true; }
118
119template <typename Atomic>
120inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
121{
122 QtTsan::futexRelease(&futex);
123 WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), INFINITE);
124 QtTsan::futexAcquire(&futex);
125}
126template <typename Atomic>
127inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
128{
129 BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(nstimeout / 1000 / 1000));
130 return r || GetLastError() != ERROR_TIMEOUT;
131}
132template <typename Atomic> inline void futexWakeAll(Atomic &futex)
133{
134 WakeByAddressAll(&futex);
135}
136template <typename Atomic> inline void futexWakeOne(Atomic &futex)
137{
138 WakeByAddressSingle(&futex);
139}
140}
141namespace QtFutex = QtWindowsFutex;
142QT_END_NAMESPACE
143#else
144
145QT_BEGIN_NAMESPACE
146namespace QtFutex = QtDummyFutex;
147QT_END_NAMESPACE
148#endif
149
150#endif // QFUTEX_P_H
151

source code of qtbase/src/corelib/thread/qfutex_p.h