blob: 2a3fb0ce87de56e237924b3f791c317d5c03a8fd [file]
// Copyright 2019 Google LLC
//
// 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 "iree/hal/host/condvar_semaphore.h"
#include <atomic>
#include <cstdint>
#include "absl/container/inlined_vector.h"
#include "absl/synchronization/mutex.h"
#include "iree/base/status.h"
#include "iree/base/tracing.h"
namespace iree {
namespace hal {
namespace host {
CondVarSemaphore::CondVarSemaphore(uint64_t initial_value)
: value_(initial_value) {}
CondVarSemaphore::~CondVarSemaphore() = default;
StatusOr<uint64_t> CondVarSemaphore::Query() {
absl::MutexLock lock(&mutex_);
if (!status_.ok()) {
return status_;
}
return value_.load(std::memory_order_acquire);
}
Status CondVarSemaphore::Signal(uint64_t value) {
absl::MutexLock lock(&mutex_);
if (!status_.ok()) {
return status_;
}
if (value_.exchange(value) >= value) {
return InvalidArgumentErrorBuilder(IREE_LOC)
<< "Semaphore values must be monotonically increasing";
}
return OkStatus();
}
void CondVarSemaphore::Fail(Status status) {
absl::MutexLock lock(&mutex_);
status_ = status;
value_.store(UINT64_MAX, std::memory_order_release);
}
// static
Status CondVarSemaphore::WaitForSemaphores(
absl::Span<const SemaphoreValue> semaphores, bool wait_all,
absl::Time deadline) {
IREE_TRACE_SCOPE0("CondVarSemaphore::WaitForSemaphores");
// Some of the semaphores may already be signaled; we only need to wait for
// those that are not yet at the expected value.
using CondVarSemaphoreValue = std::pair<CondVarSemaphore*, uint64_t>;
absl::InlinedVector<CondVarSemaphoreValue, 4> waitable_semaphores;
waitable_semaphores.reserve(semaphores.size());
for (auto& semaphore_value : semaphores) {
auto* semaphore =
reinterpret_cast<CondVarSemaphore*>(semaphore_value.semaphore);
ASSIGN_OR_RETURN(uint64_t current_value, semaphore->Query());
if (current_value < semaphore_value.value) {
// Semaphore has not yet hit the required value; wait for it.
waitable_semaphores.push_back({semaphore, semaphore_value.value});
}
}
// TODO(benvanik): maybe sort semaphores by value in case we are waiting on
// multiple values from the same semaphore.
// Loop over the semaphores and wait for them to complete.
// TODO(b/140026716): add WaitHandle support for !wait_all (wait any).
for (auto& semaphore_value : waitable_semaphores) {
auto* semaphore = semaphore_value.first;
absl::MutexLock lock(&semaphore->mutex_);
if (!semaphore->mutex_.AwaitWithDeadline(
absl::Condition(
+[](CondVarSemaphoreValue* semaphore_value) {
return semaphore_value->first->value_.load(
std::memory_order_acquire) >=
semaphore_value->second;
},
&semaphore_value),
deadline)) {
return DeadlineExceededErrorBuilder(IREE_LOC)
<< "Deadline exceeded waiting for semaphores";
}
if (!semaphore->status_.ok()) {
return semaphore->status_;
}
}
return OkStatus();
}
Status CondVarSemaphore::Wait(uint64_t value, absl::Time deadline) {
return WaitForSemaphores({{this, value}}, /*wait_all=*/true, deadline);
}
} // namespace host
} // namespace hal
} // namespace iree