21using namespace QtFutex;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
103#if defined(FUTEX_OP) && QT_POINTER_SIZE > 4
104static constexpr bool futexHasWaiterCount =
true;
106static constexpr bool futexHasWaiterCount =
false;
109static constexpr quintptr futexNeedsWakeAllBit = futexHasWaiterCount ?
110 (Q_UINT64_C(1) << (
sizeof(quintptr) * CHAR_BIT - 1)) : 0x80000000U;
112static int futexAvailCounter(quintptr v)
115 if (futexHasWaiterCount) {
117 Q_ASSERT((v & 0x80000000U) == 0);
120 return int(
unsigned(v));
122 return int(v & 0x7fffffffU);
125static bool futexNeedsWake(quintptr v)
130 if constexpr (futexHasWaiterCount)
131 return unsigned(quint64(v) >> 32) >
unsigned(v);
135static QBasicAtomicInteger<quint32> *futexLow32(QBasicAtomicInteger<quintptr> *ptr)
137 auto result =
reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
138#if Q_BYTE_ORDER == Q_BIG_ENDIAN && QT_POINTER_SIZE > 4
144static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *ptr)
146 Q_ASSERT(futexHasWaiterCount);
147 auto result =
reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
148#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN && QT_POINTER_SIZE > 4
154template <
bool IsTimed>
bool
155futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn,
156 QDeadlineTimer timer)
158 using namespace std::chrono;
159 int n =
int(
unsigned(nn));
164 auto ptr = futexLow32(&u);
165 if (n > 1 || !futexHasWaiterCount) {
166 u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
167 curValue |= futexNeedsWakeAllBit;
168 if constexpr (futexHasWaiterCount) {
170 ptr = futexHigh32(&u);
171 curValue = quint64(curValue) >> 32;
176 bool timedout = !futexWait(*ptr, curValue, timer);
180 futexWait(*ptr, curValue);
183 curValue = u.loadAcquire();
186 while (futexAvailCounter(curValue) >= n) {
187 quintptr newValue = curValue - nn;
188 if (u.testAndSetOrdered(curValue, newValue, curValue))
193 if (IsTimed && timer.hasExpired())
198static constexpr QDeadlineTimer::ForeverConstant Expired =
199 QDeadlineTimer::ForeverConstant(1);
201template <
typename T>
bool
202futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u,
int n, T timeout)
204 constexpr bool IsTimed = std::is_same_v<QDeadlineTimer, T>;
207 quintptr nn =
unsigned(n);
208 if (futexHasWaiterCount)
209 nn |= quint64(nn) << 32;
211 quintptr curValue = u.loadAcquire();
212 while (futexAvailCounter(curValue) >= n) {
214 quintptr newValue = curValue - nn;
215 if (u.testAndSetOrdered(curValue, newValue, curValue))
218 if constexpr (IsTimed) {
219 if (timeout.hasExpired())
222 if (timeout == Expired)
227 constexpr quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32);
228 if constexpr (futexHasWaiterCount) {
231 quint32 waiterCount = (quint64(curValue) >> 32) & 0x7fffffffU;
232 if (waiterCount == 0x7fffffffU) {
233 qCritical() <<
"Waiter count overflow in QSemaphore";
238 u.fetchAndAddRelaxed(oneWaiter);
239 curValue += oneWaiter;
245 if (futexSemaphoreTryAcquire_loop<IsTimed>(u, curValue, nn, timeout))
250 if (futexHasWaiterCount) {
252 Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU);
253 u.fetchAndSubRelaxed(oneWaiter);
258namespace QtSemaphorePrivate {
259using namespace QtPrivate;
262 alignas(IdealMutexAlignment) std::mutex mutex;
264 alignas(IdealMutexAlignment) std::condition_variable cond;
269 alignas(IdealMutexAlignment) std::mutex mutex;
270 alignas(IdealMutexAlignment) std::condition_variable cond;
276using Members = std::conditional_t<
sizeof(Layout1) <=
sizeof(Layout2), Layout1, Layout2>;
279class QSemaphorePrivate :
public QtSemaphorePrivate::Members
282 explicit QSemaphorePrivate(qsizetype n) { avail = n; }
286
287
288
289
290
291QSemaphore::QSemaphore(
int n)
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;
300 d =
new QSemaphorePrivate(n);
305
306
307
308
309
310QSemaphore::~QSemaphore()
312 if (!futexAvailable())
317
318
319
320
321
322
323void QSemaphore::acquire(
int n)
325#if QT_VERSION >= QT_VERSION_CHECK(7
, 0
, 0
)
326# warning "Move the Q_ASSERT to inline code, make QSemaphore have wide contract, "
327 "and mark noexcept where futexes are in use."
329 Q_ASSERT_X(n >= 0,
"QSemaphore::acquire",
"parameter 'n' must be non-negative");
332 if (futexAvailable()) {
333 futexSemaphoreTryAcquire(u, n, QDeadlineTimer::Forever);
337 const auto sufficientResourcesAvailable = [
this, n] {
return d->avail >= n; };
339 auto locker = qt_unique_lock(d->mutex);
340 d->cond.wait(locker, sufficientResourcesAvailable);
345
346
347
348
349
350
351
352
353
354
355
356
357void QSemaphore::release(
int n)
359 Q_ASSERT_X(n >= 0,
"QSemaphore::release",
"parameter 'n' must be non-negative");
361 if (futexAvailable()) {
362 quintptr nn =
unsigned(n);
363 if (futexHasWaiterCount)
364 nn |= quint64(nn) << 32;
365 quintptr prevValue = u.loadRelaxed();
368 newValue = prevValue + nn;
369 newValue &= (futexNeedsWakeAllBit - 1);
370 }
while (!u.testAndSetRelease(prevValue, newValue, prevValue));
371 if (futexNeedsWake(prevValue)) {
373 if (futexHasWaiterCount) {
375
376
377
378
379
380
381
382
383
384
385
386
387
388 quint32 op = FUTEX_OP_OR;
390 quint32 cmp = FUTEX_OP_CMP_NE;
392 futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
403 futexWakeAll(*futexLow32(&u));
404 if (futexHasWaiterCount)
405 futexWakeAll(*futexHigh32(&u));
412 const auto locker = qt_scoped_lock(d->mutex);
414 d->cond.notify_all();
418
419
420
421
422
423int QSemaphore::available()
const
425 if (futexAvailable())
426 return futexAvailCounter(u.loadRelaxed());
428 const auto locker = qt_scoped_lock(d->mutex);
433
434
435
436
437
438
439
440
441
442
443bool QSemaphore::tryAcquire(
int n)
445 Q_ASSERT_X(n >= 0,
"QSemaphore::tryAcquire",
"parameter 'n' must be non-negative");
447 if (futexAvailable())
448 return futexSemaphoreTryAcquire(u, n, Expired);
450 const auto locker = qt_scoped_lock(d->mutex);
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
477
478
479
480
481
482
483
484
485
486
487
488
489bool QSemaphore::tryAcquire(
int n, QDeadlineTimer timer)
491 if (timer.isForever()) {
496 if (timer.hasExpired())
497 return tryAcquire(n);
499 Q_ASSERT_X(n >= 0,
"QSemaphore::tryAcquire",
"parameter 'n' must be non-negative");
501 if (futexAvailable())
502 return futexSemaphoreTryAcquire(u, n, timer);
504 using namespace std::chrono;
505 const auto sufficientResourcesAvailable = [
this, n] {
return d->avail >= n; };
507 auto locker = qt_unique_lock(d->mutex);
508 if (!d->cond.wait_until(locker, timer.deadline<steady_clock>(), sufficientResourcesAvailable))
515
516
517
518
521
522
523
524
525
526
527
528
529
530
533
534
535
536
537
538
539
540
541
542
543
546
547
548
549
550
551
552
553
554
555
556
557
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
606
607
608
609
612
613
614
615
616
619
620
621
622
623
626
627
628
629
630
631
632
635
636
637
638
639
640
641
642
643
644
647
648
649
650
651
654
655
656
657
658
659
660
661
662
665
666
667
668
669
672
673
674
675
676
677
678
679
680
681
690QSemaphore::QSemaphore(
int)
695QSemaphore::~QSemaphore()
700void QSemaphore::acquire(
int)
705void QSemaphore::release(
int)
710int QSemaphore::available()
const
712 return std::numeric_limits<
int>::max();
715bool QSemaphore::tryAcquire(
int)
720bool QSemaphore::tryAcquire(
int, QDeadlineTimer)
Combined button and popup list for selecting options.