blob: 50aa38aef68879adb40c06d6efb0430f221a11d2 [file]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_async2/dispatcher_for_test.h"
#include "pw_assert/check.h"
namespace pw::async2 {
namespace {
using DispatcherForTestImpl =
DispatcherForTestFacade<backend::NativeDispatcherForTest>;
} // namespace
template <>
DispatcherForTestImpl::IdleTask::~IdleTask() {
PW_CHECK(!IsRegistered());
}
template <>
DispatcherForTestImpl::~DispatcherForTestFacade() {
PW_CHECK_INT_EQ(blocking_until_released_.load(std::memory_order_relaxed), 0);
Terminate();
}
template <>
Poll<> DispatcherForTestImpl::IdleTask::DoPend(Context& cx) {
// Store the waker before checking should_complete_ to avoid a race with the
// Complete function.
PW_ASYNC_STORE_WAKER(cx, waker_, "DispatcherForTest waiting for Release()");
if (should_complete_.load(std::memory_order_relaxed)) {
return Ready();
}
return Pending();
}
template <>
bool DispatcherForTestImpl::DoRunUntilStalled() {
bool has_posted_tasks;
Task* task;
while ((task = PopTaskToRun(has_posted_tasks)) != nullptr) {
tasks_polled_.fetch_add(1, std::memory_order_relaxed);
if (RunTask(*task) == RunTaskResult::kCompleted) {
tasks_completed_.fetch_add(1, std::memory_order_relaxed);
}
}
return has_posted_tasks;
}
template <>
void DispatcherForTestImpl::RunToCompletionUntilReleased() {
int prior = blocking_until_released_.fetch_add(1, std::memory_order_acquire);
PW_CHECK_INT_GE(prior, -1);
PW_CHECK_INT_LE(prior, 0);
bool orig_blocking_is_allowed_ = std::exchange(blocking_is_allowed_, true);
Post(idle_task_);
RunToCompletion();
idle_task_.Reset();
blocking_is_allowed_ = orig_blocking_is_allowed_;
}
template <>
void DispatcherForTestImpl::Release() {
idle_task_.Complete();
int prior = blocking_until_released_.fetch_sub(1, std::memory_order_release);
PW_CHECK_INT_GE(prior, 0);
PW_CHECK_INT_LE(prior, 1);
}
template <>
void DispatcherForTestImpl::DoWake() {
wake_count_.fetch_add(1, std::memory_order_relaxed);
native().DoWake();
}
template <>
void DispatcherForTestImpl::DoWaitForWake() {
PW_CHECK(
blocking_is_allowed_,
"The DispatcherForTest attempted to block a thread to wait for a wake. "
"If this is intentional, call dispatcher.AllowBlocking() to allow it.");
native().DoWaitForWake();
}
} // namespace pw::async2