8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/TargetInfo.h"
11#include "pedigree/kernel/process/PerProcessorScheduler.h"
12#include "pedigree/kernel/process/RoundRobin.h"
13#include "pedigree/kernel/process/Scheduler.h"
14#include "pedigree/kernel/process/Semaphore.h"
15#include "pedigree/kernel/process/SignalEvent.h"
16#include "pedigree/kernel/process/Thread.h"
17#include "pedigree/kernel/process/WaitQueue.h"
18#include "pedigree/kernel/processor/Processor.h"
19#include "pedigree/kernel/processor/ProcessorInformation.h"
20#include "pedigree/kernel/time/Time.h"
21#include "pedigree/kernel/utilities/RequestQueue.h"
23#include "modules/drivers/common/ata/AtaController.h"
26constexpr size_t RequestQueueSignalNumber = 11;
27constexpr size_t WaitAttempts = 10000;
31void hostedRequestQueueSignalHandler(
size_t) {
32 g_RequestQueueSignalCalls += 1;
35bool check(
bool condition,
const char* detail) {
40 ERROR(
"HOSTED-WAIT-TEST: FAIL requestqueue-lifecycle: " << detail);
44bool waitUntilQueued(
Thread* thread,
size_t debugState) {
45 for (
size_t attempt = 0; attempt < WaitAttempts; ++attempt) {
47 uintptr_t debugAddress = 0;
73 :
RequestQueue(MakeConstantString(
"Hosted wait regression")),
76 queuedCancellations(0),
80 selfHaltRejections(0),
81 cancelHaltRejections(0),
82 cancelResumeRejections(0),
83 cancelPublicationRejections(0),
84 cancelPreallocatedRejections(0),
87 matchEqualPayload(false) {}
89 ~HostedRequestQueue()
override {
104 PreallocatedRequest cancelPreallocated;
105 uint64_t recorded[16] = {};
109 size_t requestWaiterCount() {
110 return m_RequestQueueWaiters.waiterCount();
114 auto guard = m_RequestQueueWaiters.acquire();
118 void setMaxAsyncRequests(
size_t maximum) {
119 m_nMaxAsyncRequests = maximum;
122 void setMatchEqualPayload(
bool match) {
123 matchEqualPayload = match;
127 uint64_t
executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t, uint64_t, uint64_t,
128 uint64_t, uint64_t)
override {
133 case SelfSubmitInner:
136 return addRequest(0, SelfSubmitInner, p2, p3);
139 selfHaltRejections += 1;
144 case PreallocatedHold:
146 return releaseHold.
acquire() ? p2 : 0;
149 const size_t index = (recordedCount += 1) - 1;
150 if (index < (
sizeof(recorded) /
sizeof(recorded[0]))) {
151 recorded[index] = p2;
155 if (p1 == RecordHold) {
170 if (request.p1 == CancelQueued || request.p1 == CancelLifecycleProbe) {
171 queuedCancellations += 1;
173 if (request.p1 == CancelLifecycleProbe) {
175 cancelHaltRejections += 1;
178 cancelResumeRejections += 1;
180 if (
addRequest(0, CancelLifecycleProbe) == 0) {
181 cancelPublicationRejections += 1;
184 cancelPublicationRejections += 1;
187 PreallocatedPublishResult::QueueStopped &&
188 cancelPreallocated.isAvailable()) {
189 cancelPreallocatedRejections += 1;
196 return matchEqualPayload && a.p1 == b.p1 && a.p2 == b.p2 && a.p3 == b.p3 && a.p4 == b.p4 &&
197 a.p5 == b.p5 && a.p6 == b.p6 && a.p7 == b.p7 && a.p8 == b.p8;
201 bool matchEqualPayload;
211 HostedAtaRequestQueue()
212 :
RequestQueue(MakeConstantString(
"Hosted ATA request identity")),
217 ~HostedAtaRequestQueue()
override {
226 uint64_t
executeRequest(uint64_t, uint64_t, uint64_t location, uint64_t, uint64_t, uint64_t,
227 uint64_t, uint64_t)
override {
228 const size_t execution = executions += 1;
229 if (execution == 1) {
239 return AtaRequestIdentityProbe::canCoalesceCanonicalRequests(a.p1, a.p2, a.p3, b.p1, b.p2,
244bool ataRequestIdentityRegression() {
245 constexpr uint64_t DiskA = 0xA7A;
246 constexpr uint64_t DiskB = 0xB7B;
247 constexpr uint64_t FirstPartitionPage = 0x600;
249 constexpr uint64_t SecondPartitionExtent = FirstPartitionPage + (128 * 1024);
251 HostedAtaRequestQueue writeQueue;
252 writeQueue.initialise();
253 bool passed = writeQueue.addAsyncRequest(0, SCSI_REQUEST_WRITE, DiskA, FirstPartitionPage) == 1 &&
254 writeQueue.firstStarted.acquire();
255 const bool repeatedWriteAccepted =
256 writeQueue.addAsyncRequest(0, SCSI_REQUEST_WRITE, DiskA, FirstPartitionPage) == 1;
257 const bool distinctWritePageAccepted =
258 writeQueue.addAsyncRequest(0, SCSI_REQUEST_WRITE, DiskA, SecondPartitionPage) == 1;
259 writeQueue.releaseFirst.release();
260 const bool writesDrained = writeQueue.drain();
261 writeQueue.destroy();
262 passed &= repeatedWriteAccepted && distinctWritePageAccepted && writesDrained &&
263 writeQueue.executions == 3;
265 HostedAtaRequestQueue syncQueue;
266 syncQueue.initialise();
267 passed &= syncQueue.addAsyncRequest(0, SCSI_REQUEST_SYNC, DiskA, FirstPartitionPage) == 1 &&
268 syncQueue.firstStarted.acquire();
269 const bool repeatedSyncAccepted =
270 syncQueue.addAsyncRequest(0, SCSI_REQUEST_SYNC, DiskA, FirstPartitionPage) == 1;
271 syncQueue.releaseFirst.release();
272 const bool syncsDrained = syncQueue.drain();
274 passed &= repeatedSyncAccepted && syncsDrained && syncQueue.executions == 2;
276 HostedAtaRequestQueue readQueue;
277 readQueue.initialise();
278 passed &= readQueue.addAsyncRequest(0, SCSI_REQUEST_READ, DiskA, FirstPartitionPage) == 1 &&
279 readQueue.firstStarted.acquire();
280 const bool repeatedReadCoalesced =
281 readQueue.addAsyncRequest(0, SCSI_REQUEST_READ, DiskA, FirstPartitionPage) == 0;
282 const bool distinctExtentAccepted =
283 readQueue.addAsyncRequest(0, SCSI_REQUEST_READ, DiskA, SecondPartitionExtent) == 1;
284 const bool differentTypeAccepted =
285 readQueue.addAsyncRequest(0, SCSI_REQUEST_WRITE, DiskA, FirstPartitionPage) == 1;
286 const bool differentDiskAccepted =
287 readQueue.addAsyncRequest(0, SCSI_REQUEST_READ, DiskB, FirstPartitionPage) == 1;
288 readQueue.releaseFirst.release();
289 const bool readsDrained = readQueue.drain();
291 passed &= repeatedReadCoalesced && distinctExtentAccepted && differentTypeAccepted &&
292 differentDiskAccepted && readsDrained && readQueue.executions == 4;
294 if (!check(passed,
"ATA request identity dropped write or sync work, or merged distinct reads")) {
298 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-request-identity");
302bool watchdogProgressRegression() {
303 HostedRequestQueue queue;
306 bool passed = queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
307 queue.holdStarted.acquire();
308 passed &= queue.addAsyncRequest(0, HostedRequestQueue::Sum, 20, 22) == 1;
309 const RequestQueue::OverrunStatus transient = queue.sampleOverrunForTest();
311 queue.releaseHold.release();
312 const bool firstDrained = queue.drain();
313 const RequestQueue::OverrunStatus cleared = queue.sampleOverrunForTest();
315 passed &= queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
316 queue.holdStarted.acquire();
317 passed &= queue.addAsyncRequest(0, HostedRequestQueue::Sum, 19, 23) == 1;
318 const RequestQueue::OverrunStatus baseline = queue.sampleOverrunForTest();
319 const RequestQueue::OverrunStatus stalled = queue.sampleOverrunForTest();
321 queue.releaseHold.release();
322 const bool secondDrained = queue.drain();
325 passed &= transient == RequestQueue::OverrunStatus::Armed && firstDrained &&
326 cleared == RequestQueue::OverrunStatus::Clear &&
327 baseline == RequestQueue::OverrunStatus::Armed &&
328 stalled == RequestQueue::OverrunStatus::Stalled && secondDrained;
330 "the watchdog confused transient admission with a full "
331 "no-progress interval")) {
335 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-watchdog-progress");
339struct HeldRequestContext {
340 explicit HeldRequestContext(HostedRequestQueue* queue)
341 : queue(queue), finished(0), result(~0ULL) {}
343 HostedRequestQueue* queue;
348struct CompletionRequeueContext {
349 explicit CompletionRequeueContext(
Thread* target)
350 : target(target), requeues(0), hookFailures(0) {}
357CompletionRequeueContext* g_CompletionRequeueContext =
nullptr;
361 CompletionRequeueContext* context = g_CompletionRequeueContext;
362 if (!context || thread != context->target) {
366 if (!queue || channel.owner || channel.value || debugState != Thread::CondWait) {
367 context->hookFailures += 1;
369 context->requeues += 1;
372int submitHeldRequest(
void* parameter) {
373 HeldRequestContext* context =
reinterpret_cast<HeldRequestContext*
>(parameter);
374 context->result = context->queue->addRequest(0, HostedRequestQueue::HoldWorker, 42);
375 context->finished += 1;
379bool completionBarrierInterruption(HostedRequestQueue& queue,
bool terminal) {
380 HeldRequestContext request(&queue);
382 nullptr,
false,
true);
384 caller->setName(
"hosted RequestQueue terminal caller");
386 caller->setName(
"hosted RequestQueue signal caller");
389 const bool workerHeld = queue.holdStarted.acquire();
390 const bool initiallyQueued = waitUntilQueued(caller, Thread::CondWait);
392 CompletionRequeueContext requeue(caller);
393 g_CompletionRequeueContext = &requeue;
394 WaitQueue::setBeforeBlockHook(completionRequeueHook);
396 bool injected =
true;
400 g_RequestQueueSignalCalls = 0;
402 new SignalEvent(
reinterpret_cast<uintptr_t
>(&hostedRequestQueueSignalHandler),
403 RequestQueueSignalNumber, ~0UL, 0,
true,
true);
410 for (
size_t attempt = 0; attempt < WaitAttempts && !requeue.requeues && !request.finished;
415 WaitQueue::setBeforeBlockHook(
nullptr);
416 g_CompletionRequeueContext =
nullptr;
418 const bool deferred = requeue.requeues == 1 && requeue.hookFailures == 0 && request.finished == 0;
419 const bool signalDelivered = terminal || g_RequestQueueSignalCalls == 1;
421 queue.releaseHold.release();
422 const bool joined = caller->
join();
424 return check(workerHeld && initiallyQueued && injected && deferred && signalDelivered && joined &&
425 request.finished == 1 && request.result == 42,
426 terminal ?
"terminal teardown escaped a synchronous request completion"
427 :
"a signal returned a synchronous request before completion");
430struct RequestQueueDestroyContext {
431 explicit RequestQueueDestroyContext(HostedRequestQueue* queue) : queue(queue), finished(0) {}
433 HostedRequestQueue* queue;
437struct RequestQueueDrainContext {
438 explicit RequestQueueDrainContext(HostedRequestQueue* queue)
439 : queue(queue), finished(0), result(false) {}
441 HostedRequestQueue* queue;
446struct QueuedRequestContext {
447 explicit QueuedRequestContext(HostedRequestQueue* queue)
448 : queue(queue),
waiter(nullptr), published(0), hookFailures(0), finished(0), result(~0ULL) {}
450 HostedRequestQueue* queue;
458QueuedRequestContext* g_QueuedRequestContext =
nullptr;
462 QueuedRequestContext* context = g_QueuedRequestContext;
467 if (thread != context->waiter) {
471 if (!queue || channel.owner || channel.value || debugState != Thread::CondWait) {
472 context->hookFailures += 1;
474 context->published += 1;
477int submitQueuedRequest(
void* parameter) {
478 QueuedRequestContext* context =
reinterpret_cast<QueuedRequestContext*
>(parameter);
480 context->result = context->queue->addRequest(0, HostedRequestQueue::CancelLifecycleProbe);
481 context->finished += 1;
485int destroyRequestQueue(
void* parameter) {
486 RequestQueueDestroyContext* context =
reinterpret_cast<RequestQueueDestroyContext*
>(parameter);
487 context->queue->destroy();
488 context->finished += 1;
492int drainRequestQueue(
void* parameter) {
493 RequestQueueDrainContext* context =
reinterpret_cast<RequestQueueDrainContext*
>(parameter);
494 context->result = context->queue->drain();
495 context->finished += 1;
499struct RequestQueueHaltContext {
500 explicit RequestQueueHaltContext(HostedRequestQueue* queue)
501 : queue(queue), finished(0), result(false) {}
503 HostedRequestQueue* queue;
508int haltRequestQueue(
void* parameter) {
509 auto* context =
reinterpret_cast<RequestQueueHaltContext*
>(parameter);
510 context->result = context->queue->halt() ? 1 : 0;
511 context->finished += 1;
515struct PublicationPauseContext {
516 PublicationPauseContext() : calls(0), failures(0), entered(0), release(0) {}
524void pauseFirstPublication(
void* parameter) {
525 auto* context =
reinterpret_cast<PublicationPauseContext*
>(parameter);
526 if ((context->calls += 1) != 1) {
530 context->entered.release();
531 if (!context->release.acquireForCompletion()) {
532 context->failures += 1;
536struct PreallocatedPublicationContext {
537 PreallocatedPublicationContext(HostedRequestQueue* queue,
539 uint64_t operation, uint64_t value)
543 operation(operation),
545 result(
RequestQueue::PreallocatedPublishResult::QueueStopped),
548 HostedRequestQueue* queue;
553 RequestQueue::PreallocatedPublishResult result;
557int publishPreallocatedRequest(
void* parameter) {
558 auto* context =
reinterpret_cast<PreallocatedPublicationContext*
>(parameter);
559 context->result = context->queue->publishPreallocated(*context->request, context->priority,
560 context->operation, context->value);
561 context->finished += 1;
565struct AsyncPublicationContext {
566 AsyncPublicationContext(HostedRequestQueue* queue,
size_t priority, uint64_t operation,
570 operation(operation),
575 HostedRequestQueue* queue;
583int publishAsyncRequest(
void* parameter) {
584 auto* context =
reinterpret_cast<AsyncPublicationContext*
>(parameter);
586 context->queue->addAsyncRequest(context->priority, context->operation, context->value);
587 context->finished += 1;
591struct PreallocatedReleaseContext {
592 PreallocatedReleaseContext(HostedRequestQueue* queue,
bool requeueOnce,
bool holdFirst =
false,
593 bool probeOrdinaryPublication =
false)
596 requeueOnce(requeueOnce),
597 holdFirst(holdFirst),
598 probeOrdinaryPublication(probeOrdinaryPublication),
601 ordinaryPublicationRejections(0),
604 releaseCallback(0) {}
606 HostedRequestQueue* queue;
610 bool probeOrdinaryPublication;
619void preallocatedRequestReleased(
void* parameter) {
620 auto* context =
reinterpret_cast<PreallocatedReleaseContext*
>(parameter);
621 const size_t callback = (context->callbacks += 1);
622 if (!context->request || context->request->isAvailable()) {
623 context->failures += 1;
626 if (context->holdFirst && callback == 1) {
627 context->callbackEntered.release();
628 if (!context->releaseCallback.acquireForCompletion()) {
629 context->failures += 1;
634 if (context->probeOrdinaryPublication) {
636 if (context->queue->publishPreallocated(ordinary, 0, HostedRequestQueue::Sum, 20, 22) ==
637 RequestQueue::PreallocatedPublishResult::QueueStopped &&
638 ordinary.isAvailable()) {
639 context->ordinaryPublicationRejections += 1;
641 context->failures += 1;
645 if (context->request && context->requeueOnce && context->requeues.compareAndSwap(0, 1)) {
646 if (context->queue->republishPreallocatedWhileReleasing(*context->request, 0,
647 HostedRequestQueue::Sum, 20, 22) !=
648 RequestQueue::PreallocatedPublishResult::Accepted) {
649 context->failures += 1;
654struct ReleaseDestroyReentryContext {
655 explicit ReleaseDestroyReentryContext(HostedRequestQueue* queue)
661 allocationRejections(0),
662 preallocatedRejections(0),
666 HostedRequestQueue* queue;
678void releaseDuringDestroy(
void* parameter) {
679 auto* context =
reinterpret_cast<ReleaseDestroyReentryContext*
>(parameter);
680 context->callbacks += 1;
681 context->entered.release();
682 if (!context->release.acquireForCompletion()) {
683 context->failures += 1;
687 if (!context->queue->halt()) {
688 context->haltRejections += 1;
690 if (!context->queue->resume()) {
691 context->resumeRejections += 1;
693 if (context->queue->addRequest(0, HostedRequestQueue::Sum, 20, 22) == 0 &&
694 context->queue->addAsyncRequest(0, HostedRequestQueue::Sum, 20, 22) == 0) {
695 context->allocationRejections += 1;
697 if (context->queue->publishPreallocated(context->ordinary, 0, HostedRequestQueue::Sum, 20, 22) ==
698 RequestQueue::PreallocatedPublishResult::QueueStopped &&
699 context->ordinary.isAvailable()) {
700 context->preallocatedRejections += 1;
704bool predicateDoorbellRegression() {
705 using Result = RequestQueue::PreallocatedPublishResult;
707 HostedRequestQueue queue;
713 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
714 const size_t waitersBefore = queue.requestWaiterCount();
717 const Result accepted = queue.publishPreallocated(request, 0, HostedRequestQueue::Sum, 20, 22);
718 const Result busy = queue.publishPreallocated(request, 0, HostedRequestQueue::Sum, 19, 23);
719 const bool doorbellPending = PerProcessorScheduler::currentIrqWorkDoorbellPendingForTest();
720 const bool deferred = queue.executions == 0;
721 const size_t waitersAfterPublication = queue.requestWaiterCount();
724 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
725 const bool drained = queue.drain();
726 const size_t executions = queue.executions;
727 const bool available = request.isAvailable();
731 check(waitersBefore == 0 && waitersAfterPublication == 0 && accepted == Result::Accepted &&
732 busy == Result::TokenBusy && doorbellPending && deferred && drained &&
733 executions == 1 && available,
734 "IF=0 publication touched a WaitQueue or escaped the predicate "
737 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-predicate-doorbell");
742bool preallocatedDuplicateDomainRegression() {
743 using Result = RequestQueue::PreallocatedPublishResult;
745 HostedRequestQueue queue;
747 queue.setMatchEqualPayload(
true);
750 bool passed = queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
751 queue.holdStarted.acquire();
755 const Result preallocatedAccepted =
756 queue.publishPreallocated(preallocated, 1, HostedRequestQueue::Record, 77, 88);
759 const size_t comparisonsBefore = queue.comparisons;
760 const bool allocatedAccepted = queue.addAsyncRequest(1, HostedRequestQueue::Record, 77, 88) == 1;
761 const size_t comparisonsAfter = queue.comparisons;
763 queue.releaseHold.release();
764 const bool drained = queue.drain();
765 const bool executedBoth =
766 queue.recordedCount == 2 && queue.recorded[0] == 77 && queue.recorded[1] == 77;
769 passed &= preallocatedAccepted == Result::Accepted && allocatedAccepted &&
770 comparisonsBefore == 0 && comparisonsAfter == 0 && drained && executedBoth &&
771 preallocated.isAvailable() && queue.cancellations == 0 && queue.executions == 3;
772 passed = check(passed,
773 "allocation-backed duplicate comparison entered the preallocated "
775 "coalescing domain");
778 "HOSTED-WAIT-TEST: PASS "
779 "requestqueue-preallocated-duplicate-domain");
784bool intakeOrderingRegression() {
785 using Result = RequestQueue::PreallocatedPublishResult;
787 HostedRequestQueue queue;
792 bool passed = queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
793 queue.holdStarted.acquire();
794 passed &= queue.addAsyncRequest(3, HostedRequestQueue::Record, 30) == 1;
795 passed &= queue.addAsyncRequest(1, HostedRequestQueue::Record, 10) == 1;
799 const Result firstAccepted =
800 queue.publishPreallocated(firstPreallocated, 1, HostedRequestQueue::Record, 11);
803 passed &= queue.addAsyncRequest(1, HostedRequestQueue::Record, 12) == 1;
805 const Result secondAccepted =
806 queue.publishPreallocated(secondPreallocated, 1, HostedRequestQueue::Record, 13);
809 passed &= queue.addAsyncRequest(2, HostedRequestQueue::Record, 20) == 1;
810 passed &= queue.addAsyncRequest(0, HostedRequestQueue::Record, 1) == 1;
812 queue.releaseHold.release();
813 const bool drained = queue.drain();
814 const uint64_t expected[] = {1, 10, 11, 12, 13, 20, 30};
815 bool orderMatches = queue.recordedCount == (
sizeof(expected) /
sizeof(expected[0]));
817 for (
size_t i = 0; i < (
sizeof(expected) /
sizeof(expected[0])); ++i) {
818 orderMatches &= queue.recorded[i] == expected[i];
822 passed &= firstAccepted == Result::Accepted && secondAccepted == Result::Accepted && drained &&
823 orderMatches && queue.recordFailures == 0 && queue.executions == 8 &&
824 firstPreallocated.isAvailable() && secondPreallocated.isAvailable();
827 passed = check(passed,
"the shared intake lost same-priority FIFO or strict priority order");
829 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-intake-ordering");
834bool haltRetentionRegression() {
835 using Result = RequestQueue::PreallocatedPublishResult;
837 HostedRequestQueue queue;
842 bool passed = queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
843 queue.holdStarted.acquire();
844 passed &= queue.addAsyncRequest(1, HostedRequestQueue::Record, 40) == 1;
848 const Result retainedResult =
849 queue.publishPreallocated(retained, 1, HostedRequestQueue::Record, 41);
852 RequestQueueHaltContext haltContext(&queue);
854 &haltContext,
nullptr,
false,
true);
855 halter->setName(
"hosted RequestQueue halt retention regression");
857 bool stopping =
false;
858 for (
size_t attempt = 0; attempt < WaitAttempts; ++attempt) {
859 stopping = queue.getLifecycleState() == RequestQueue::LifecycleState::Stopping;
866 Result rejectedResult = Result::Accepted;
869 rejectedResult = queue.publishPreallocated(rejected, 1, HostedRequestQueue::Record, 42);
872 const bool retainedBeforeRelease = queue.recordedCount == 0 && !retained.isAvailable();
873 queue.releaseHold.release();
875 const bool haltFinished = haltContext.finished == 1;
876 const bool haltSucceeded = haltContext.result == 1;
877 const bool stopped = queue.getLifecycleState() == RequestQueue::LifecycleState::Stopped;
878 const bool retainedWhileStopped = queue.recordedCount == 0 && !retained.isAvailable();
880 const bool resumed = queue.resume();
881 const bool drained = resumed && queue.drain();
882 const bool resumedInOrder =
883 queue.recordedCount == 2 && queue.recorded[0] == 40 && queue.recorded[1] == 41;
886 passed &= check(retainedResult == Result::Accepted,
887 "halt retention setup did not admit preallocated work");
888 passed &= check(stopping,
"halt did not publish the Stopping lifecycle state");
889 passed &= check(rejectedResult == Result::QueueStopped && rejected.isAvailable(),
890 "halt did not close preallocated admission");
891 passed &= check(retainedBeforeRelease,
892 "accepted work ran or was released before the active worker exited");
893 passed &= check(halterJoined,
"halt regression thread could not be joined");
894 passed &= check(haltFinished,
"halt regression thread did not return exactly once");
895 passed &= check(haltSucceeded,
"halt rejected its non-worker caller");
896 passed &= check(stopped,
"successful halt did not publish Stopped");
897 passed &= check(retainedWhileStopped,
898 "halt executed or released accepted work instead of retaining it");
899 passed &= check(resumed,
"halted queue rejected resume");
900 passed &= check(drained,
"resumed queue did not drain retained work");
902 check(resumedInOrder,
"resumed queue did not execute retained work in publication order");
903 passed &= check(retained.isAvailable(),
"resumed preallocated work did not release its token");
904 passed &= check(queue.recordFailures == 0 && queue.executions == 3,
905 "halt retention executed an unexpected request set");
907 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-halt-retention");
912bool rejectedPublisherDestroyWaitRegression() {
913 using Result = RequestQueue::PreallocatedPublishResult;
915 HostedRequestQueue queue;
917 PublicationPauseContext pause;
918 queue.setAfterPreallocatedAdmissionHookForTest(pauseFirstPublication, &pause);
920 PreallocatedPublicationContext publication(&queue, &request, 1, HostedRequestQueue::Record, 16);
922 publishPreallocatedRequest, &publication,
nullptr,
false,
true);
923 publisher->setName(
"hosted RequestQueue rejected publication pause");
924 const bool paused = pause.entered.acquire();
926 RequestQueueDestroyContext destroyContext(&queue);
928 &destroyContext,
nullptr,
false,
true);
929 destroyer->setName(
"hosted RequestQueue stopped publication drain regression");
931 bool drainWaitObserved =
false;
932 for (
size_t attempt = 0; attempt < WaitAttempts; ++attempt) {
933 drainWaitObserved = queue.publisherDrainRetriesForTest() != 0;
934 if (drainWaitObserved) {
940 const bool destroyWaited = drainWaitObserved && !publication.finished &&
941 !destroyContext.finished && request.isAvailable();
942 pause.release.release();
943 const bool publisherJoined = publisher->
join();
944 const bool destroyerJoined = destroyer->
join();
945 queue.setAfterPreallocatedAdmissionHookForTest(
nullptr,
nullptr);
947 const bool passed = check(
948 paused && destroyWaited && publisherJoined && destroyerJoined && publication.finished == 1 &&
949 publication.result == Result::QueueStopped && destroyContext.finished == 1 &&
950 request.isAvailable() && pause.calls == 1 && pause.failures == 0 &&
951 queue.executions == 0 && queue.cancellations == 0 && queue.recordedCount == 0,
952 "a never-started queue outran a rejected publisher in its closed-gate "
956 "HOSTED-WAIT-TEST: PASS "
957 "requestqueue-rejected-publication-close-wait");
962bool preallocatedAdmissionCloseWaitRegression() {
963 using Result = RequestQueue::PreallocatedPublishResult;
965 HostedRequestQueue queue;
967 PublicationPauseContext pause;
969 queue.setAfterPreallocatedAdmissionHookForTest(pauseFirstPublication, &pause);
971 PreallocatedPublicationContext publication(&queue, &request, 1, HostedRequestQueue::Record, 17);
973 publishPreallocatedRequest, &publication,
nullptr,
false,
true);
974 publisher->setName(
"hosted RequestQueue admitted publication pause");
975 const bool paused = pause.entered.acquire();
977 RequestQueueDestroyContext destroyContext(&queue);
979 &destroyContext,
nullptr,
false,
true);
980 destroyer->setName(
"hosted RequestQueue publication drain regression");
982 bool stopping =
false;
983 for (
size_t attempt = 0; attempt < WaitAttempts; ++attempt) {
984 stopping = queue.getLifecycleState() == RequestQueue::LifecycleState::Stopping;
991 const bool destroyWaited =
992 stopping && !publication.finished && !destroyContext.finished && request.isAvailable();
993 pause.release.release();
994 const bool publisherJoined = publisher->
join();
995 const bool destroyerJoined = destroyer->
join();
996 queue.setAfterPreallocatedAdmissionHookForTest(
nullptr,
nullptr);
998 const bool passed = check(
999 paused && destroyWaited && publisherJoined && destroyerJoined && publication.finished == 1 &&
1000 publication.result == Result::Accepted && destroyContext.finished == 1 &&
1001 request.isAvailable() && pause.calls == 1 && pause.failures == 0 &&
1002 queue.executions == 0 && queue.cancellations == 1 && queue.recordedCount == 0,
1003 "destroy outran an admitted preallocated publisher paused before its "
1004 "closed-gate observation");
1007 "HOSTED-WAIT-TEST: PASS "
1008 "requestqueue-preallocated-admission-close-wait");
1013bool intakeTransientPublicationRegression() {
1014 using Result = RequestQueue::PreallocatedPublishResult;
1016 HostedRequestQueue queue;
1018 PublicationPauseContext pause;
1021 bool passed = queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
1022 queue.holdStarted.acquire();
1023 passed &= queue.addAsyncRequest(3, HostedRequestQueue::Record, 30) == 1;
1024 queue.setAfterIntakeExchangeHookForTest(pauseFirstPublication, &pause);
1026 PreallocatedPublicationContext first(&queue, &preallocated, 1, HostedRequestQueue::RecordHold,
1028 Thread* preallocatedPublisher =
1030 nullptr,
false,
true);
1031 preallocatedPublisher->setName(
"hosted RequestQueue incomplete MPSC publisher");
1032 const bool paused = pause.entered.acquire();
1034 AsyncPublicationContext second(&queue, 1, HostedRequestQueue::Record, 11);
1036 publishAsyncRequest, &second,
nullptr,
false,
true);
1037 ordinaryPublisher->setName(
"hosted RequestQueue transient duplicate scanner");
1039 queue.releaseHold.release();
1040 bool bothRetried =
false;
1041 for (
size_t attempt = 0; attempt < WaitAttempts; ++attempt) {
1042 bothRetried = queue.workerTransientRetriesForTest() && queue.guardedTransientRetriesForTest();
1049 const bool noOvertake = queue.recordedCount == 0 && !first.finished && !second.finished;
1050 pause.release.release();
1051 const bool preallocatedJoined = preallocatedPublisher->
join();
1052 const bool firstHeld = queue.holdStarted.acquire();
1053 const bool ordinaryJoined = ordinaryPublisher->
join();
1054 queue.setAfterIntakeExchangeHookForTest(
nullptr,
nullptr);
1056 const bool firstStillLeads = firstHeld && queue.recordedCount == 1 && queue.recorded[0] == 10;
1057 queue.releaseHold.release();
1058 const bool drained = queue.drain();
1059 const uint64_t expected[] = {10, 11, 30};
1060 bool orderMatches = queue.recordedCount == (
sizeof(expected) /
sizeof(expected[0]));
1062 for (
size_t i = 0; i < (
sizeof(expected) /
sizeof(expected[0])); ++i) {
1063 orderMatches &= queue.recorded[i] == expected[i];
1068 passed &= paused && bothRetried && noOvertake && preallocatedJoined && ordinaryJoined &&
1069 first.result == Result::Accepted && first.finished == 1 && second.result == 1 &&
1070 second.finished == 1 && firstStillLeads && drained && orderMatches &&
1071 preallocated.isAvailable() && pause.calls == 2 && pause.failures == 0 &&
1072 queue.executions == 4 && queue.cancellations == 0 && queue.recordFailures == 0;
1073 passed = check(passed,
1074 "an incomplete MPSC link was treated as empty or lost priority/FIFO "
1078 "HOSTED-WAIT-TEST: PASS "
1079 "requestqueue-intake-transient-publication");
1084bool preallocatedRequestRegressions() {
1085 using Result = RequestQueue::PreallocatedPublishResult;
1089 HostedRequestQueue queue;
1090 PreallocatedReleaseContext releaseContext(&queue,
false,
true);
1092 releaseContext.request = &released;
1093 queue.setMaxAsyncRequests(0);
1096 passed &= check(queue.republishPreallocatedWhileReleasing(
1097 released, 0, HostedRequestQueue::Sum) == Result::TokenBusy &&
1098 released.isAvailable(),
1099 "an idle token accepted release-only republication");
1101 queue.publishPreallocated(released, 0, HostedRequestQueue::Sum, 19, 23) == Result::Accepted,
1102 "release-callback request was not admitted");
1103 passed &= check(releaseContext.callbackEntered.acquire(),
1104 "release callback did not enter its drain handoff");
1106 RequestQueueDrainContext drainContext(&queue);
1108 &drainContext,
nullptr,
false,
true);
1109 drainer->setName(
"hosted preallocated release drain regression");
1110 const bool drainWaitPublished = waitUntilQueued(drainer, Thread::CallbackDrain);
1111 passed &= check(drainWaitPublished && drainContext.finished == 0,
1112 "drain observed a transient empty queue during release");
1114 passed &= check(queue.republishPreallocatedWhileReleasing(released, 0, HostedRequestQueue::Sum,
1115 20, 22) == Result::Accepted &&
1116 !released.isAvailable(),
1117 "a producer could not win the final release handoff");
1118 releaseContext.releaseCallback.release();
1119 passed &= check(drainer->
join() && drainContext.result && released.isAvailable() &&
1120 releaseContext.callbacks == 2 && releaseContext.requeues == 0 &&
1121 releaseContext.failures == 0 && queue.executions == 2,
1122 "producer-assisted release work escaped drain");
1127 HostedRequestQueue queue;
1128 PreallocatedReleaseContext releaseContext(&queue,
true,
false,
true);
1130 releaseContext.request = &released;
1131 queue.setMaxAsyncRequests(0);
1134 passed &= check(queue.publishPreallocated(released, 0, HostedRequestQueue::Sum, 19, 23) ==
1136 queue.drain() && released.isAvailable() && releaseContext.callbacks == 2 &&
1137 releaseContext.requeues == 1 &&
1138 releaseContext.ordinaryPublicationRejections == 2 &&
1139 releaseContext.failures == 0 && queue.executions == 2,
1140 "release callback did not reject ordinary publication while "
1141 "preserving the explicit Releasing-token handoff");
1146 HostedRequestQueue queue;
1150 passed &= check(queue.publishPreallocated(request, 0, HostedRequestQueue::Sum, 20, 22) ==
1151 Result::QueueStopped &&
1152 request.isAvailable(),
1153 "a stopped queue claimed a preallocated token");
1154 passed &= check(queue.publishPreallocated(request, REQUEST_QUEUE_NUM_PRIORITIES,
1155 HostedRequestQueue::Sum) == Result::InvalidPriority &&
1156 request.isAvailable(),
1157 "an invalid preallocated priority claimed its token");
1161 const size_t comparisonsBefore = queue.comparisons;
1163 const Result accepted = queue.publishPreallocated(request, 0, HostedRequestQueue::Sum, 20, 22);
1164 const Result busy = queue.publishPreallocated(request, 0, HostedRequestQueue::Sum, 19, 23);
1165 const bool deferred = queue.executions == 0;
1167 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
1169 passed &= check(accepted == Result::Accepted && busy == Result::TokenBusy && deferred &&
1170 static_cast<size_t>(queue.comparisons) == comparisonsBefore,
1171 "preallocated IF=0 enqueue ran early or called virtual duplicate "
1173 passed &= check(queue.drain() && request.isAvailable() && queue.executions == 1,
1174 "executed preallocated work did not release its token");
1176 passed &= check(queue.publishPreallocated(request, 0, HostedRequestQueue::PreallocatedHold,
1177 42) == Result::Accepted &&
1178 queue.holdStarted.acquire(),
1179 "preallocated hold request was not admitted");
1180 passed &= check(queue.publishPreallocated(request, 0, HostedRequestQueue::PreallocatedHold,
1181 42) == Result::TokenBusy,
1182 "a published preallocated token was admitted twice");
1183 queue.setMaxAsyncRequests(1);
1184 const size_t cancellationsBeforeCapacity = queue.cancellations;
1186 queue.publishPreallocated(capacity, 0, HostedRequestQueue::Sum, 1, 2) == Result::Accepted &&
1187 !capacity.isAvailable() &&
1188 queue.addAsyncRequest(0, HostedRequestQueue::Sum, 3, 4) == 0 &&
1189 queue.cancellations == cancellationsBeforeCapacity + 1,
1190 "preallocated work depended on allocation backlog capacity");
1191 queue.setMaxAsyncRequests(256);
1192 queue.releaseHold.release();
1194 queue.drain() && request.isAvailable() && capacity.isAvailable() && queue.executions == 3,
1195 "over-capacity preallocated work did not drain exactly once");
1198 queue.publishPreallocated(capacity, 0, HostedRequestQueue::Sum, 19, 23) == Result::Accepted,
1199 "a drained preallocated token was not admitted again");
1200 passed &= check(queue.drain() && capacity.isAvailable() && queue.executions == 4,
1201 "a drained preallocated token could not be reused");
1206 HostedRequestQueue queue;
1207 PreallocatedReleaseContext releaseContext(&queue,
false);
1209 releaseContext.request = &cancelled;
1211 passed &= check(queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
1212 queue.holdStarted.acquire(),
1213 "teardown setup did not hold the worker");
1214 passed &= check(queue.publishPreallocated(cancelled, 0, HostedRequestQueue::CancelQueued) ==
1216 "teardown setup did not queue the preallocated token");
1218 RequestQueueDestroyContext destroyContext(&queue);
1220 &destroyContext,
nullptr,
false,
true);
1221 destroyer->setName(
"hosted preallocated request destroy regression");
1222 while (queue.getLifecycleState() != RequestQueue::LifecycleState::Stopping) {
1225 queue.releaseHold.release();
1227 passed &= check(destroyer->
join() && destroyContext.finished == 1 && cancelled.isAvailable() &&
1228 queue.cancellations == 1 && queue.queuedCancellations == 1 &&
1229 releaseContext.callbacks == 1 && releaseContext.failures == 0,
1230 "destroy did not cancel and release queued preallocated work");
1232 queue.publishPreallocated(cancelled, 0, HostedRequestQueue::Sum) == Result::QueueStopped &&
1233 cancelled.isAvailable(),
1234 "a stopped queue retained a reused preallocated token");
1238 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-preallocated-token");
1243bool releaseCallbackDestroyReentryRegression() {
1244 using Result = RequestQueue::PreallocatedPublishResult;
1246 HostedRequestQueue queue;
1247 ReleaseDestroyReentryContext releaseContext(&queue);
1251 const bool accepted =
1252 queue.publishPreallocated(released, 0, HostedRequestQueue::Sum, 20, 22) == Result::Accepted;
1253 const bool callbackEntered = accepted && releaseContext.entered.acquire();
1255 RequestQueueDestroyContext destroyContext(&queue);
1257 &destroyContext,
nullptr,
false,
true);
1258 destroyer->setName(
"hosted RequestQueue release callback destroy regression");
1260 bool stopping =
false;
1261 for (
size_t attempt = 0; attempt < WaitAttempts; ++attempt) {
1262 stopping = queue.getLifecycleState() == RequestQueue::LifecycleState::Stopping;
1269 const bool destroyWaitedForRelease = stopping && !destroyContext.finished;
1270 releaseContext.release.release();
1274 check(callbackEntered && destroyWaitedForRelease && destroyerJoined &&
1275 destroyContext.finished == 1 && releaseContext.callbacks == 1 &&
1276 releaseContext.failures == 0 && releaseContext.haltRejections == 1 &&
1277 releaseContext.resumeRejections == 1 && releaseContext.allocationRejections == 1 &&
1278 releaseContext.preallocatedRejections == 1 && released.isAvailable() &&
1279 releaseContext.ordinary.isAvailable() && queue.executions == 1 &&
1280 queue.cancellations == 0 &&
1281 queue.getLifecycleState() == RequestQueue::LifecycleState::Destroyed,
1282 "a worker release callback re-entered queue lifecycle or publication "
1283 "while destroy waited for it");
1286 "HOSTED-WAIT-TEST: PASS "
1287 "requestqueue-release-callback-destroy-reentry");
1293bool runHostedRequestQueueRegressions() {
1295 if (!check(RoundRobin::runHostedIntrusiveQueueRegressions(current),
1296 "intrusive scheduler ready-queue invariants failed")) {
1299 NOTICE(
"HOSTED-WAIT-TEST: PASS scheduler-intrusive-ready-queue");
1301 if (!ataRequestIdentityRegression()) {
1305 if (!preallocatedRequestRegressions()) {
1309 if (!releaseCallbackDestroyReentryRegression()) {
1313 if (!predicateDoorbellRegression()) {
1317 if (!preallocatedDuplicateDomainRegression()) {
1321 if (!intakeOrderingRegression()) {
1325 if (!haltRetentionRegression()) {
1329 if (!rejectedPublisherDestroyWaitRegression()) {
1333 if (!preallocatedAdmissionCloseWaitRegression()) {
1337 if (!intakeTransientPublicationRegression()) {
1341 if (!watchdogProgressRegression()) {
1346 HostedRequestQueue activeRequestQueue;
1347 activeRequestQueue.initialise();
1348 const bool accepted =
1349 activeRequestQueue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1;
1350 const bool active = accepted && activeRequestQueue.holdStarted.acquire();
1352 const Time::Timestamp deadline =
1353 Time::getTicks() + Time::Multiplier::Second + (100 * Time::Multiplier::Millisecond);
1354 while (Time::getTicks() < deadline) {
1358 activeRequestQueue.releaseHold.release();
1359 const bool drained = activeRequestQueue.drain();
1360 activeRequestQueue.destroy();
1361 if (!check(accepted && active && drained &&
1362 activeRequestQueue.executions ==
static_cast<size_t>(1),
1363 "an active request was misclassified as queued backlog")) {
1366 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-active-not-backlog");
1370 HostedRequestQueue ownedWorkerQueue;
1371 ownedWorkerQueue.initialise();
1372 Thread* idleWorker = ownedWorkerQueue.workerThread();
1376 const bool idleTerminalSafe =
1377 idleWorker && ownedWorkerQueue.addRequest(0, HostedRequestQueue::Sum, 20, 22) == 42;
1379 const bool halted = ownedWorkerQueue.halt();
1380 const bool resumed = ownedWorkerQueue.resume();
1381 Thread* activeWorker = ownedWorkerQueue.workerThread();
1383 ownedWorkerQueue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1 &&
1384 ownedWorkerQueue.holdStarted.acquire();
1388 ownedWorkerQueue.releaseHold.release();
1389 const bool activeTerminalSafe =
1390 held && activeWorker &&
1391 ownedWorkerQueue.addRequest(0, HostedRequestQueue::Sum, 19, 23) == 42;
1392 ownedWorkerQueue.destroy();
1394 if (!check(idleTerminalSafe && halted && resumed && activeTerminalSafe,
1395 "queue-owned worker termination orphaned work")) {
1398 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-worker-terminal-ownership");
1401 HostedRequestQueue queue;
1404 passed &= check(queue.addRequest(0, HostedRequestQueue::Sum, 1, 2) == 0,
1405 "a stopped queue accepted work before initialise");
1408 passed &= check(queue.getLifecycleState() == RequestQueue::LifecycleState::Accepting,
1409 "initialise did not start the worker");
1411 queue.addRequest(REQUEST_QUEUE_NUM_PRIORITIES - 1, HostedRequestQueue::Sum, 40, 2) == 42,
1412 "synchronous completion returned the wrong result");
1414 check(queue.addRequest(REQUEST_QUEUE_NUM_PRIORITIES, HostedRequestQueue::Sum, 1, 2) == 0,
1415 "an invalid priority was accepted");
1417 passed &= check(queue.halt(),
"halt rejected its non-worker caller");
1418 passed &= check(queue.getLifecycleState() == RequestQueue::LifecycleState::Stopped,
1419 "halt did not join the worker");
1420 passed &= check(queue.addRequest(0, HostedRequestQueue::Sum, 3, 4) == 0,
1421 "a halted queue accepted work");
1423 passed &= check(queue.resume(),
"resume rejected a halted queue");
1424 passed &= check(queue.addRequest(0, HostedRequestQueue::Sum, 20, 22) == 42,
1425 "the resumed worker did not complete synchronous work");
1426 passed &= check(queue.addRequest(0, HostedRequestQueue::SelfSubmit, 19, 23) == 42,
1427 "worker self-submission did not execute inline");
1428 passed &= check(queue.addRequest(0, HostedRequestQueue::SelfHalt, 77) == 77 &&
1429 queue.selfHaltRejections == 1 &&
1430 queue.getLifecycleState() == RequestQueue::LifecycleState::Accepting &&
1431 queue.addRequest(0, HostedRequestQueue::Sum, 20, 22) == 42,
1432 "worker self-halt did not reject without stopping the queue");
1434 passed &= completionBarrierInterruption(queue,
false);
1435 passed &= completionBarrierInterruption(queue,
true);
1437 passed &= check(queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1,
1438 "the drain test request was not accepted");
1439 passed &= check(queue.holdStarted.acquire(),
"the drain test worker did not enter its request");
1440 RequestQueueDrainContext drainContext(&queue);
1442 &drainContext,
nullptr,
false,
true);
1443 drainer->setName(
"hosted RequestQueue drain regression");
1444 const bool drainWaitPublished = waitUntilQueued(drainer, Thread::CallbackDrain);
1445 const bool drainBlocked = drainContext.finished == 0;
1446 queue.releaseHold.release();
1447 passed &= check(drainer->
join() && drainWaitPublished && drainBlocked &&
1448 drainContext.finished == 1 && drainContext.result,
1449 "drain returned before all published work completed");
1451 passed &= check(queue.addAsyncRequest(0, HostedRequestQueue::HoldWorker, 42) == 1,
1452 "the blocking worker request was not accepted");
1453 passed &= check(queue.holdStarted.acquire(),
"the worker did not enter its held request");
1455 QueuedRequestContext queuedContext(&queue);
1456 g_QueuedRequestContext = &queuedContext;
1457 WaitQueue::setBeforeBlockHook(queuedRequestWaitHook);
1459 &queuedContext,
nullptr,
false,
true);
1460 submitter->setName(
"hosted RequestQueue queued caller");
1462 while (queuedContext.published < 1) {
1465 WaitQueue::setBeforeBlockHook(
nullptr);
1466 g_QueuedRequestContext =
nullptr;
1468 RequestQueueDestroyContext destroyContext(&queue);
1470 &destroyContext,
nullptr,
false,
true);
1471 destroyer->setName(
"hosted RequestQueue destroy regression");
1473 while (queue.getLifecycleState() != RequestQueue::LifecycleState::Stopping) {
1476 queue.releaseHold.release();
1478 passed &= check(destroyer->
join(),
"the concurrent destroy worker could not be joined");
1479 passed &= check(submitter->
join(),
"the cancelled synchronous caller could not be joined");
1480 passed &= check(destroyContext.finished == 1,
"destroy did not complete exactly once");
1481 passed &= check(queuedContext.published == 1 && queuedContext.hookFailures == 0,
1482 "the queued synchronous caller did not publish one completion wait");
1483 passed &= check(queuedContext.finished == 1 && queuedContext.result == 0,
1484 "destroy did not wake and reject the queued synchronous caller");
1486 check(queue.cancelHaltRejections == 1 && queue.cancelResumeRejections == 1 &&
1487 queue.cancelPublicationRejections == 2 && queue.cancelPreallocatedRejections == 1 &&
1488 queue.cancelPreallocated.isAvailable(),
1489 "destroy cancellation did not reject recursive lifecycle entry");
1492 passed &= check(queue.getLifecycleState() == RequestQueue::LifecycleState::Destroyed,
1493 "destroy was not idempotent");
1495 const bool resumeRejected = !queue.resume();
1498 const RequestQueue::PreallocatedPublishResult postDestroyResult =
1499 queue.publishPreallocated(postDestroy, 0, HostedRequestQueue::Sum, 20, 22);
1501 passed &= check(resumeRejected &&
1502 queue.getLifecycleState() == RequestQueue::LifecycleState::Destroyed &&
1503 queue.addRequest(0, HostedRequestQueue::Sum, 20, 22) == 0 &&
1504 postDestroyResult == RequestQueue::PreallocatedPublishResult::QueueStopped &&
1505 postDestroy.isAvailable(),
1506 "destroyed queue reopened publication");
1508 passed &= check(queue.getLifecycleState() == RequestQueue::LifecycleState::Destroyed,
1509 "repeated destroy changed the terminal lifecycle state");
1510 passed &= check(queue.executions == 10,
"the worker executed an unexpected number of requests");
1511 passed &= check(queue.cancellations == 5,
"rejected requests did not release their payloads");
1512 passed &= check(queue.queuedCancellations == 1,
1513 "destroy did not cancel the queued synchronous request");
1516 NOTICE(
"HOSTED-WAIT-TEST: PASS requestqueue-lifecycle");
static bool canCoalesceCanonicalRequests(uint64_t aType, uint64_t aDisk, uint64_t aLocation, uint64_t bType, uint64_t bDisk, uint64_t bLocation)
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
virtual bool compareRequests(const Request &a, const Request &b)
MUST_USE_RESULT bool halt()
MUST_USE_RESULT bool resume()
MUST_USE_RESULT PreallocatedPublishResult publishPreallocated(PreallocatedRequest &request, size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
virtual void cancelRequest(const Request &request)
virtual uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)=0
MUST_USE_RESULT uint64_t addRequest(size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
uint64_t addAsyncRequest(size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
static Scheduler & instance()
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
static constexpr size_t getPageSize() noexcept
void setUnwindState(UnwindType ut)
@ TerminateThread
Exit only this thread during Process exit.
bool getWaitDebugInfo(WaitDebugInfo &info)
DebugState getDebugState(uintptr_t &address)
bool sendEvent(Event *pEvent)