blob: bbac4a0480356d470dea6ed06e541d9d46cc5f8f [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/audio/lib/clock/synthetic_timer.h"
#include <lib/syslog/cpp/macros.h>
#include "src/media/audio/lib/clock/scoped_unique_lock.h"
namespace media_audio {
// static
std::shared_ptr<SyntheticTimer> SyntheticTimer::Create(zx::time mono_start_time) {
struct MakePublicCtor : SyntheticTimer {
MakePublicCtor(zx::time mono_start_time) : SyntheticTimer(mono_start_time) {}
};
return std::make_shared<MakePublicCtor>(mono_start_time);
}
SyntheticTimer::SyntheticTimer(zx::time mono_start_time) : state_(mono_start_time) {}
void SyntheticTimer::SetEventBit() {
std::lock_guard<std::mutex> lock(mutex_);
state_.event_set = true;
}
void SyntheticTimer::SetShutdownBit() {
std::lock_guard<std::mutex> lock(mutex_);
state_.shutdown_set = true;
}
Timer::WakeReason SyntheticTimer::SleepUntil(zx::time deadline) {
scoped_unique_lock<std::mutex> lock(mutex_);
FX_CHECK(!state_.stopped);
WakeReason wake_reason;
do {
// Notify WaitUntilSleepingOrStopped that we are sleeping, then wait for AdvanceTo.
state_.deadline_if_sleeping = deadline;
state_.sleep_count++;
state_.cvar.notify_all();
state_.cvar.wait(
lock, [this]() TA_REQ(mutex_) { return state_.advance_count == state_.sleep_count; });
wake_reason = {
.deadline_expired = state_.now >= deadline,
.event_set = state_.event_set,
.shutdown_set = state_.shutdown_set,
};
// Try again if there's no reason to wake.
} while (!wake_reason.deadline_expired && !wake_reason.event_set && !wake_reason.shutdown_set);
// No longer sleeping.
state_.deadline_if_sleeping = std::nullopt;
state_.wake_count++;
state_.cvar.notify_all();
// Need to clear before returning: see timer.h.
state_.event_set = false;
return wake_reason;
}
void SyntheticTimer::Stop() {
std::lock_guard<std::mutex> lock(mutex_);
state_.stopped = true;
state_.cvar.notify_all();
}
void SyntheticTimer::WaitUntilSleepingOrStopped() {
scoped_unique_lock<std::mutex> lock(mutex_);
state_.cvar.wait(
lock, [this]() TA_REQ(mutex_) { return state_.deadline_if_sleeping || state_.stopped; });
}
void SyntheticTimer::AdvanceTo(zx::time t) {
scoped_unique_lock<std::mutex> lock(mutex_);
FX_CHECK(state_.deadline_if_sleeping || state_.stopped)
<< "Must be called while the Timer is sleeping or stopped";
FX_CHECK(t >= state_.now) << "Cannot go backwards from " << state_.now.get() << " to " << t.get();
// Advance the current time.
state_.now = t;
// Don't wake SleepUntil unless there is a pending signal or the deadline has expired.
if (state_.stopped ||
(t < *state_.deadline_if_sleeping && !state_.event_set && !state_.shutdown_set)) {
return;
}
state_.advance_count++;
state_.cvar.notify_all();
// Wait until SleepUntil returns so that commands which happen-after this function
// call won't be observed by the sleeper. For example, given a sequence:
//
// timer.AdvanceTo(x) ---- wakes ----> timer.SleepUntil
// timer.SetEventBit()
//
// Assuming the timer's event bit is not initially set, the SleepUntil call should
// not report `event_set = true`.
state_.cvar.wait(lock,
[this]() TA_REQ(mutex_) { return state_.wake_count == state_.advance_count; });
}
SyntheticTimer::State SyntheticTimer::CurrentState() {
std::lock_guard<std::mutex> lock(mutex_);
return {
.deadline = state_.deadline_if_sleeping,
.event_set = state_.event_set,
.shutdown_set = state_.shutdown_set,
.stopped = state_.stopped,
};
}
zx::time SyntheticTimer::now() {
std::lock_guard<std::mutex> lock(mutex_);
return state_.now;
}
} // namespace media_audio