18using namespace QtFutex;
23
24
25
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
100#if defined(FUTEX_OP) && QT_POINTER_SIZE > 4
101static constexpr bool futexHasWaiterCount =
true;
103static constexpr bool futexHasWaiterCount =
false;
106static constexpr quintptr futexNeedsWakeAllBit = futexHasWaiterCount ?
107 (Q_UINT64_C(1) << (
sizeof(quintptr) * CHAR_BIT - 1)) : 0x80000000U;
109static int futexAvailCounter(quintptr v)
112 if (futexHasWaiterCount) {
114 Q_ASSERT((v & 0x80000000U) == 0);
117 return int(
unsigned(v));
119 return int(v & 0x7fffffffU);
122static bool futexNeedsWake(quintptr v)
127 if constexpr (futexHasWaiterCount)
128 return unsigned(quint64(v) >> 32) >
unsigned(v);
132static QBasicAtomicInteger<quint32> *futexLow32(QBasicAtomicInteger<quintptr> *ptr)
134 auto result =
reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
135#if Q_BYTE_ORDER == Q_BIG_ENDIAN && QT_POINTER_SIZE > 4
141static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *ptr)
143 Q_ASSERT(futexHasWaiterCount);
144 auto result =
reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
145#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN && QT_POINTER_SIZE > 4
151template <
bool IsTimed>
bool
152futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn,
153 QDeadlineTimer timer)
155 using namespace std::chrono;
156 int n =
int(
unsigned(nn));
161 auto ptr = futexLow32(&u);
162 if (n > 1 || !futexHasWaiterCount) {
163 u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
164 curValue |= futexNeedsWakeAllBit;
165 if constexpr (futexHasWaiterCount) {
167 ptr = futexHigh32(&u);
168 curValue = quint64(curValue) >> 32;
173 bool timedout = !futexWait(*ptr, curValue, timer);
177 futexWait(*ptr, curValue);
180 curValue = u.loadAcquire();
183 while (futexAvailCounter(curValue) >= n) {
184 quintptr newValue = curValue - nn;
185 if (u.testAndSetOrdered(curValue, newValue, curValue))
190 if (IsTimed && timer.hasExpired())
195static constexpr QDeadlineTimer::ForeverConstant Expired =
196 QDeadlineTimer::ForeverConstant(1);
198template <
typename T>
bool
199futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u,
int n, T timeout)
201 constexpr bool IsTimed = std::is_same_v<QDeadlineTimer, T>;
204 quintptr nn =
unsigned(n);
205 if (futexHasWaiterCount)
206 nn |= quint64(nn) << 32;
208 quintptr curValue = u.loadAcquire();
209 while (futexAvailCounter(curValue) >= n) {
211 quintptr newValue = curValue - nn;
212 if (u.testAndSetOrdered(curValue, newValue, curValue))
215 if constexpr (IsTimed) {
216 if (timeout.hasExpired())
219 if (timeout == Expired)
224 constexpr quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32);
225 if constexpr (futexHasWaiterCount) {
228 quint32 waiterCount = (quint64(curValue) >> 32) & 0x7fffffffU;
229 if (waiterCount == 0x7fffffffU) {
230 qCritical() <<
"Waiter count overflow in QSemaphore";
235 u.fetchAndAddRelaxed(oneWaiter);
236 curValue += oneWaiter;
242 if (futexSemaphoreTryAcquire_loop<IsTimed>(u, curValue, nn, timeout))
247 if (futexHasWaiterCount) {
249 Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU);
250 u.fetchAndSubRelaxed(oneWaiter);
255namespace QtSemaphorePrivate {
256using namespace QtPrivate;
259 alignas(IdealMutexAlignment) std::mutex mutex;
261 alignas(IdealMutexAlignment) std::condition_variable cond;
266 alignas(IdealMutexAlignment) std::mutex mutex;
267 alignas(IdealMutexAlignment) std::condition_variable cond;
273using Members = std::conditional_t<
sizeof(Layout1) <=
sizeof(Layout2), Layout1, Layout2>;
276class QSemaphorePrivate :
public QtSemaphorePrivate::Members
279 explicit QSemaphorePrivate(qsizetype n) { avail = n; }
283
284
285
286
287
288QSemaphore::QSemaphore(
int n)
290 Q_ASSERT_X(n >= 0,
"QSemaphore",
"parameter 'n' must be non-negative");
291 if (futexAvailable()) {
292 quintptr nn =
unsigned(n);
293 if (futexHasWaiterCount)
294 nn |= quint64(nn) << 32;
297 d =
new QSemaphorePrivate(n);
302
303
304
305
306
307QSemaphore::~QSemaphore()
309 if (!futexAvailable())
314
315
316
317
318
319
320void QSemaphore::acquire(
int n)
322#if QT_VERSION >= QT_VERSION_CHECK(7
, 0
, 0
)
323# warning "Move the Q_ASSERT to inline code, make QSemaphore have wide contract, "
324 "and mark noexcept where futexes are in use."
326 Q_ASSERT_X(n >= 0,
"QSemaphore::acquire",
"parameter 'n' must be non-negative");
329 if (futexAvailable()) {
330 futexSemaphoreTryAcquire(u, n, QDeadlineTimer::Forever);
334 const auto sufficientResourcesAvailable = [
this, n] {
return d->avail >= n; };
336 auto locker = qt_unique_lock(d->mutex);
337 d->cond.wait(locker, sufficientResourcesAvailable);
342
343
344
345
346
347
348
349
350
351
352
353
354void QSemaphore::release(
int n)
356 Q_ASSERT_X(n >= 0,
"QSemaphore::release",
"parameter 'n' must be non-negative");
358 if (futexAvailable()) {
359 quintptr nn =
unsigned(n);
360 if (futexHasWaiterCount)
361 nn |= quint64(nn) << 32;
362 quintptr prevValue = u.loadRelaxed();
365 newValue = prevValue + nn;
366 newValue &= (futexNeedsWakeAllBit - 1);
367 }
while (!u.testAndSetRelease(prevValue, newValue, prevValue));
368 if (futexNeedsWake(prevValue)) {
370 if (futexHasWaiterCount) {
372
373
374
375
376
377
378
379
380
381
382
383
384
385 quint32 op = FUTEX_OP_OR;
387 quint32 cmp = FUTEX_OP_CMP_NE;
389 futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
400 futexWakeAll(*futexLow32(&u));
401 if (futexHasWaiterCount)
402 futexWakeAll(*futexHigh32(&u));
409 const auto locker = qt_scoped_lock(d->mutex);
411 d->cond.notify_all();
415
416
417
418
419
420int QSemaphore::available()
const
422 if (futexAvailable())
423 return futexAvailCounter(u.loadRelaxed());
425 const auto locker = qt_scoped_lock(d->mutex);
430
431
432
433
434
435
436
437
438
439
440bool QSemaphore::tryAcquire(
int n)
442 Q_ASSERT_X(n >= 0,
"QSemaphore::tryAcquire",
"parameter 'n' must be non-negative");
444 if (futexAvailable())
445 return futexSemaphoreTryAcquire(u, n, Expired);
447 const auto locker = qt_scoped_lock(d->mutex);
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
474
475
476
477
478
479
480
481
482
483
484
485
486bool QSemaphore::tryAcquire(
int n, QDeadlineTimer timer)
488 if (timer.isForever()) {
493 if (timer.hasExpired())
494 return tryAcquire(n);
496 Q_ASSERT_X(n >= 0,
"QSemaphore::tryAcquire",
"parameter 'n' must be non-negative");
498 if (futexAvailable())
499 return futexSemaphoreTryAcquire(u, n, timer);
501 using namespace std::chrono;
502 const auto sufficientResourcesAvailable = [
this, n] {
return d->avail >= n; };
504 auto locker = qt_unique_lock(d->mutex);
505 if (!d->cond.wait_until(locker, timer.deadline<steady_clock>(), sufficientResourcesAvailable))
512
513
514
515
518
519
520
521
522
523
524
525
526
527
530
531
532
533
534
535
536
537
538
539
540
543
544
545
546
547
548
549
550
551
552
553
554
557
558
559
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
603
604
605
606
609
610
611
612
613
616
617
618
619
620
623
624
625
626
627
628
629
632
633
634
635
636
637
638
639
640
641
644
645
646
647
648
651
652
653
654
655
656
657
658
659
662
663
664
665
666
669
670
671
672
673
674
675
676
677
678
687QSemaphore::QSemaphore(
int n)
692QSemaphore::~QSemaphore()
697void QSemaphore::acquire(
int)
702void QSemaphore::release(
int)