blob: 668f30b2b2fa009b477d2a27a2416d27a3bdebc4 [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_clock_realm.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <cmath>
#include <string>
namespace media_audio {
// static
std::shared_ptr<SyntheticClockRealm> SyntheticClockRealm::Create() {
struct MakePublicCtor : SyntheticClockRealm {
MakePublicCtor() : SyntheticClockRealm() {}
};
return std::make_shared<MakePublicCtor>();
}
std::shared_ptr<SyntheticClock> SyntheticClockRealm::CreateClock(
std::string_view name, uint32_t domain, bool adjustable,
media::TimelineFunction to_clock_mono) {
return SyntheticClock::Create(name, domain, adjustable, shared_from_this(), to_clock_mono);
}
std::shared_ptr<SyntheticTimer> SyntheticClockRealm::CreateTimer() {
// Serialize with Advance to avoid lost updates.
std::lock_guard<std::mutex> lock1(advance_mutex_);
std::lock_guard<std::mutex> lock2(mutex_);
GarbageCollectTimers(); // prevent unbounded growth
auto timer = SyntheticTimer::Create(mono_now_);
timers_.push_back(timer);
return timer;
}
zx::time SyntheticClockRealm::now() const {
std::lock_guard<std::mutex> lock(mutex_);
return mono_now_;
}
void SyntheticClockRealm::AdvanceTo(zx::time mono_now) {
std::lock_guard<std::mutex> lock(advance_mutex_);
AdvanceToImpl(mono_now);
}
void SyntheticClockRealm::AdvanceBy(zx::duration mono_diff) {
std::lock_guard<std::mutex> lock(advance_mutex_);
AdvanceToImpl(now() + mono_diff);
}
void SyntheticClockRealm::AdvanceToImpl(zx::time target_mono_now) {
auto mono_now = now();
FX_CHECK(target_mono_now >= mono_now);
for (;;) {
zx::time next_deadline = zx::time::infinite();
bool has_signal = false;
// Instead of advancing directly to `target_mono_now`, wait until all timers are sleeping, then
// compute the next deadline and check if any signals are pending.
for (auto& weak : timers_) {
if (auto t = weak.lock(); t) {
t->WaitUntilSleepingOrStopped();
auto state = t->CurrentState();
if (state.stopped) {
continue;
}
if (state.deadline) {
next_deadline = std::min(*state.deadline, next_deadline);
}
if (state.shutdown_set || state.event_set) {
has_signal = true;
}
}
}
// If there are signals pending, process those before advancing time. Otherwise advance to the
// next deadline or `target_mono_now`, whichever is earlier. Stop when there are no signals
// pending, we've advanced to `target_mono_now`, and the next deadline is in the future.
if (!has_signal) {
std::lock_guard<std::mutex> lock(mutex_);
if (mono_now_ == target_mono_now && next_deadline > target_mono_now) {
return;
}
mono_now_ = std::min(target_mono_now, next_deadline);
mono_now = mono_now_;
}
for (auto& weak : timers_) {
if (auto t = weak.lock(); t) {
t->AdvanceTo(mono_now);
}
}
}
}
void SyntheticClockRealm::GarbageCollectTimers() {
for (auto it = timers_.begin(); it != timers_.end();) {
if (it->expired()) {
it = timers_.erase(it);
} else {
++it;
}
}
}
} // namespace media_audio