QtBase  v6.3.1
qsemaphore.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Copyright (C) 2018 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qsemaphore.h"
42 #include "qmutex.h"
43 #include "qfutex_p.h"
44 #include "qwaitcondition.h"
45 #include "qdeadlinetimer.h"
46 #include "qdatetime.h"
47 
49 
50 using namespace QtFutex;
51 
102 /*
103  QSemaphore futex operation
104 
105  QSemaphore stores a 32-bit integer with the counter of currently available
106  tokens (value between 0 and INT_MAX). When a thread attempts to acquire n
107  tokens and the counter is larger than that, we perform a compare-and-swap
108  with the new count. If that succeeds, the acquisition worked; if not, we
109  loop again because the counter changed. If there were not enough tokens,
110  we'll perform a futex-wait.
111 
112  Before we do, we set the high bit in the futex to indicate that semaphore
113  is contended: that is, there's a thread waiting for more tokens. On
114  release() for n tokens, we perform a fetch-and-add of n and then check if
115  that high bit was set. If it was, then we clear that bit and perform a
116  futex-wake on the semaphore to indicate the waiting threads can wake up and
117  acquire tokens. Which ones get woken up is unspecified.
118 
119  If the system has the ability to wake up a precise number of threads, has
120  Linux's FUTEX_WAKE_OP functionality, and is 64-bit, instead of using a
121  single bit indicating a contended semaphore, we'll store the number of
122  tokens *plus* total number of waiters in the high word. Additionally, all
123  multi-token waiters will be waiting on that high word. So when releasing n
124  tokens on those systems, we tell the kernel to wake up n single-token
125  threads and all of the multi-token ones. Which threads get woken up is
126  unspecified, but it's likely single-token threads will get woken up first.
127  */
128 
129 #if defined(FUTEX_OP) && QT_POINTER_SIZE > 4
130 static constexpr bool futexHasWaiterCount = true;
131 #else
132 static constexpr bool futexHasWaiterCount = false;
133 #endif
134 
135 static constexpr quintptr futexNeedsWakeAllBit = futexHasWaiterCount ?
136  (Q_UINT64_C(1) << (sizeof(quintptr) * CHAR_BIT - 1)) : 0x80000000U;
137 
138 static int futexAvailCounter(quintptr v)
139 {
140  // the low 31 bits
141  if (futexHasWaiterCount) {
142  // the high bit of the low word isn't used
143  Q_ASSERT((v & 0x80000000U) == 0);
144 
145  // so we can be a little faster
146  return int(unsigned(v));
147  }
148  return int(v & 0x7fffffffU);
149 }
150 
151 static bool futexNeedsWake(quintptr v)
152 {
153  // If we're counting waiters, the number of waiters plus value is stored in the
154  // low 31 bits of the high word (that is, bits 32-62). If we're not, then we only
155  // use futexNeedsWakeAllBit to indicate anyone is waiting.
156  if constexpr (futexHasWaiterCount)
157  return unsigned(quint64(v) >> 32) > unsigned(v);
158  return v >> 31;
159 }
160 
162 {
163  auto result = reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
164 #if Q_BYTE_ORDER == Q_BIG_ENDIAN && QT_POINTER_SIZE > 4
165  ++result;
166 #endif
167  return result;
168 }
169 
171 {
172  Q_ASSERT(futexHasWaiterCount);
173  auto result = reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
174 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN && QT_POINTER_SIZE > 4
175  ++result;
176 #endif
177  return result;
178 }
179 
180 template <bool IsTimed> bool
182 {
184  qint64 remainingTime = timeout * Q_INT64_C(1000) * 1000;
185  int n = int(unsigned(nn));
186 
187  // we're called after one testAndSet, so start by waiting first
188  for (;;) {
189  // indicate we're waiting
190  auto ptr = futexLow32(&u);
191  if (n > 1 || !futexHasWaiterCount) {
192  u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
193  curValue |= futexNeedsWakeAllBit;
194  if constexpr (futexHasWaiterCount) {
195  Q_ASSERT(n > 1);
196  ptr = futexHigh32(&u);
197  curValue >>= 32;
198  }
199  }
200 
201  if (IsTimed && remainingTime > 0) {
202  bool timedout = !futexWait(*ptr, curValue, remainingTime);
203  if (timedout)
204  return false;
205  } else {
206  futexWait(*ptr, curValue);
207  }
208 
209  curValue = u.loadAcquire();
210  if (IsTimed)
211  remainingTime = timer.remainingTimeNSecs();
212 
213  // try to acquire
214  while (futexAvailCounter(curValue) >= n) {
215  quintptr newValue = curValue - nn;
216  if (u.testAndSetOrdered(curValue, newValue, curValue))
217  return true; // succeeded!
218  }
219 
220  // not enough tokens available, put us to wait
221  if (remainingTime == 0)
222  return false;
223  }
224 }
225 
226 template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, int timeout)
227 {
228  // Try to acquire without waiting (we still loop because the testAndSet
229  // call can fail).
230  quintptr nn = unsigned(n);
231  if (futexHasWaiterCount)
232  nn |= quint64(nn) << 32; // token count replicated in high word
233 
234  quintptr curValue = u.loadAcquire();
235  while (futexAvailCounter(curValue) >= n) {
236  // try to acquire
237  quintptr newValue = curValue - nn;
238  if (u.testAndSetOrdered(curValue, newValue, curValue))
239  return true; // succeeded!
240  }
241  if (timeout == 0)
242  return false;
243 
244  // we need to wait
245  constexpr quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
246  if constexpr (futexHasWaiterCount) {
247  // We don't use the fetched value from above so futexWait() fails if
248  // it changed after the testAndSetOrdered above.
249  if (((curValue >> 32) & 0x7fffffffU) == 0x7fffffffU) {
250  qCritical() << "Waiter count overflow in QSemaphore";
251  return false;
252  }
253 
254  // increase the waiter count
255  u.fetchAndAddRelaxed(oneWaiter);
256  curValue += oneWaiter;
257 
258  // Also adjust nn to subtract oneWaiter when we succeed in acquiring.
259  nn += oneWaiter;
260  }
261 
262  if (futexSemaphoreTryAcquire_loop<IsTimed>(u, curValue, nn, timeout))
263  return true;
264 
265  Q_ASSERT(IsTimed);
266 
267  if (futexHasWaiterCount) {
268  // decrement the number of threads waiting
269  Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU);
270  u.fetchAndSubRelaxed(oneWaiter);
271  }
272  return false;
273 }
274 
276 public:
277  inline QSemaphorePrivate(int n) : avail(n) { }
278 
281 
282  int avail;
283 };
284 
292 {
293  Q_ASSERT_X(n >= 0, "QSemaphore", "parameter 'n' must be non-negative");
294  if (futexAvailable()) {
295  quintptr nn = unsigned(n);
296  if (futexHasWaiterCount)
297  nn |= quint64(nn) << 32; // token count replicated in high word
298  u.storeRelaxed(nn);
299  } else {
300  d = new QSemaphorePrivate(n);
301  }
302 }
303 
311 {
312  if (!futexAvailable())
313  delete d;
314 }
315 
324 {
325  Q_ASSERT_X(n >= 0, "QSemaphore::acquire", "parameter 'n' must be non-negative");
326 
327  if (futexAvailable()) {
328  futexSemaphoreTryAcquire<false>(u, n, -1);
329  return;
330  }
331 
332  QMutexLocker locker(&d->mutex);
333  while (n > d->avail)
334  d->cond.wait(locker.mutex());
335  d->avail -= n;
336 }
337 
352 {
353  Q_ASSERT_X(n >= 0, "QSemaphore::release", "parameter 'n' must be non-negative");
354 
355  if (futexAvailable()) {
356  quintptr nn = unsigned(n);
357  if (futexHasWaiterCount)
358  nn |= quint64(nn) << 32; // token count replicated in high word
359  quintptr prevValue = u.loadRelaxed();
360  quintptr newValue;
361  do { // loop just to ensure the operations are done atomically
362  newValue = prevValue + nn;
363  newValue &= (futexNeedsWakeAllBit - 1);
364  } while (!u.testAndSetRelease(prevValue, newValue, prevValue));
365  if (futexNeedsWake(prevValue)) {
366 #ifdef FUTEX_OP
367  if (futexHasWaiterCount) {
368  /*
369  On 64-bit systems, the single-token waiters wait on the low half
370  and the multi-token waiters wait on the upper half. So we ask
371  the kernel to wake up n single-token waiters and all multi-token
372  waiters (if any), and clear the multi-token wait bit.
373 
374  atomic {
375  int oldval = *upper;
376  *upper = oldval | 0;
377  futexWake(lower, n);
378  if (oldval != 0) // always true
379  futexWake(upper, INT_MAX);
380  }
381  */
382  quint32 op = FUTEX_OP_OR;
383  quint32 oparg = 0;
384  quint32 cmp = FUTEX_OP_CMP_NE;
385  quint32 cmparg = 0;
386  futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
387  return;
388  }
389 #endif
390  // Unset the bit and wake everyone. There are two possibilities
391  // under which a thread can set the bit between the AND and the
392  // futexWake:
393  // 1) it did see the new counter value, but it wasn't enough for
394  // its acquisition anyway, so it has to wait;
395  // 2) it did not see the new counter value, in which case its
396  // futexWait will fail.
397  if (futexHasWaiterCount) {
398  futexWakeAll(*futexLow32(&u));
399  futexWakeAll(*futexHigh32(&u));
400  } else {
401  futexWakeAll(u);
402  }
403  }
404  return;
405  }
406 
407  QMutexLocker locker(&d->mutex);
408  d->avail += n;
409  d->cond.wakeAll();
410 }
411 
419 {
420  if (futexAvailable())
421  return futexAvailCounter(u.loadRelaxed());
422 
423  QMutexLocker locker(&d->mutex);
424  return d->avail;
425 }
426 
439 {
440  Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
441 
442  if (futexAvailable())
443  return futexSemaphoreTryAcquire<false>(u, n, 0);
444 
445  QMutexLocker locker(&d->mutex);
446  if (n > d->avail)
447  return false;
448  d->avail -= n;
449  return true;
450 }
451 
469 {
470  Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
471 
472  // We're documented to accept any negative value as "forever"
473  // but QDeadlineTimer only accepts -1.
474  timeout = qMax(timeout, -1);
475 
476  if (futexAvailable())
477  return futexSemaphoreTryAcquire<true>(u, n, timeout);
478 
480  QMutexLocker locker(&d->mutex);
481  while (n > d->avail && !timer.hasExpired()) {
482  if (!d->cond.wait(locker.mutex(), timer))
483  return false;
484  }
485  if (n > d->avail)
486  return false;
487  d->avail -= n;
488  return true;
489 }
490 
small capitals from c petite p scientific f u
Definition: afcover.h:88
The QDeadlineTimer class marks a deadline in the future.
The QMutex class provides access serialization between threads.
Definition: qmutex.h:285
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes.
Definition: qmutex.h:317
Mutex * mutex() const noexcept
Definition: qmutex.h:324
void acquire(int n=1)
Definition: qsemaphore.cpp:323
bool tryAcquire(int n=1)
Definition: qsemaphore.cpp:438
void release(int n=1)
Definition: qsemaphore.cpp:351
QSemaphore(int n=0)
Definition: qsemaphore.cpp:291
int available() const
Definition: qsemaphore.cpp:418
QWaitCondition cond
Definition: qsemaphore.cpp:280
QSemaphorePrivate(int n)
Definition: qsemaphore.cpp:277
constexpr bool futexAvailable()
Definition: qfutex_p.h:81
void futexWakeAll(Atomic &)
Definition: qfutex_p.h:87
bool futexWait(Atomic &, typename Atomic::Type, int=0)
Definition: qfutex_p.h:83
set set set set set set set macro pixldst1 op
#define Q_UINT64_C(c)
Definition: qglobal.h:296
unsigned int quint32
Definition: qglobal.h:288
size_t quintptr
Definition: qglobal.h:310
unsigned long long quint64
Definition: qglobal.h:299
long long qint64
Definition: qglobal.h:298
#define Q_INT64_C(c)
Definition: qglobal.h:295
#define qCritical
Definition: qlogging.h:180
GLsizei const GLfloat * v
[13]
GLbitfield GLuint64 timeout
[4]
GLfloat n
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define Q_ASSERT_X(cond, x, msg)
Definition: qrandom.cpp:85
bool futexSemaphoreTryAcquire(QBasicAtomicInteger< quintptr > &u, int n, int timeout)
Definition: qsemaphore.cpp:226
bool futexSemaphoreTryAcquire_loop(QBasicAtomicInteger< quintptr > &u, quintptr curValue, quintptr nn, int timeout)
Definition: qsemaphore.cpp:181
QTimer * timer
[3]