blob: 72802d6c8fffbe3e48eb1a0211d2fbe21baf6037 [file] [log] [blame]
// Copyright 2019 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/ui/scenic/lib/flatland/uber_struct_system.h"
#include <lib/syslog/cpp/macros.h>
namespace flatland {
// |UberStructSystem| implementations.
TransformHandle::InstanceId UberStructSystem::GetNextInstanceId() {
// |latest_instance_id_| is only used for tests, but returning a member value can result in
// threads "stealing" instance IDs from each other, so we return a local value here instead,
// which does not have the same risk.
auto next_instance_id = scheduling::GetNextSessionId();
latest_instance_id_ = next_instance_id;
return next_instance_id;
}
std::shared_ptr<UberStructSystem::UberStructQueue> UberStructSystem::AllocateQueueForSession(
scheduling::SessionId session_id) {
FX_DCHECK(!pending_structs_queues_.count(session_id));
auto [queue_kv, success] =
pending_structs_queues_.emplace(session_id, std::make_shared<UberStructQueue>());
FX_DCHECK(success);
return queue_kv->second;
}
void UberStructSystem::RemoveSession(scheduling::SessionId session_id) {
FX_DCHECK(pending_structs_queues_.count(session_id));
pending_structs_queues_.erase(session_id);
uber_struct_map_.erase(session_id);
}
scheduling::SessionUpdater::UpdateResults UberStructSystem::UpdateSessions(
const std::unordered_map<scheduling::SessionId, scheduling::PresentId>& sessions_to_update) {
scheduling::SessionUpdater::UpdateResults results;
for (const auto& [session_id, present_id] : sessions_to_update) {
// Find the queue associated with this SessonId. It may not exist if the SessionId is
// associated with a GFX session instead of a Flatland one.
auto queue_kv = pending_structs_queues_.find(session_id);
if (queue_kv == pending_structs_queues_.end()) {
continue;
}
// Pop entries from that queue until the correct PresentId is found, then commit that
// UberStruct to the snapshot. If the next pending UberStruct has a PresentId greater than the
// target one, the update has failed because PresentIds are strictly increasing.
bool successful_update = false;
auto pending_struct = queue_kv->second->Pop();
while (pending_struct.has_value()) {
if (pending_struct->present_id == present_id) {
uber_struct_map_[session_id] = std::move(pending_struct->uber_struct);
successful_update = true;
break;
} else if (pending_struct->present_id > present_id) {
break;
}
pending_struct = queue_kv->second->Pop();
}
if (!successful_update) {
results.sessions_with_failed_updates.insert(session_id);
}
}
return results;
}
void UberStructSystem::ForceUpdateAllSessions(size_t max_updates_per_queue) {
// Pop entries from each queue until empty.
for (auto& [session_id, queue] : pending_structs_queues_) {
size_t update_count = 0;
auto pending_struct = queue->Pop();
while (pending_struct.has_value()) {
uber_struct_map_[session_id] = std::move(pending_struct.value().uber_struct);
pending_struct = queue->Pop();
if (++update_count > max_updates_per_queue) {
break;
}
}
}
}
UberStruct::InstanceMap UberStructSystem::Snapshot() { return uber_struct_map_; }
size_t UberStructSystem::GetSessionCount() { return pending_structs_queues_.size(); }
TransformHandle::InstanceId UberStructSystem::GetLatestInstanceId() const {
return latest_instance_id_;
}
// |UberStructSystem::Queue| implementations.
void UberStructSystem::UberStructQueue::Push(scheduling::PresentId present_id,
std::unique_ptr<UberStruct> uber_struct) {
FX_DCHECK(uber_struct);
// Acquire the lock and update the appropriate queue.
{
std::scoped_lock lock(queue_mutex_);
// PresentIds must be strictly increasing
FX_DCHECK(pending_structs_.empty() || pending_structs_.back().present_id < present_id);
pending_structs_.push({.present_id = present_id, .uber_struct = std::move(uber_struct)});
}
}
std::optional<UberStructSystem::PendingUberStruct> UberStructSystem::UberStructQueue::Pop() {
std::optional<PendingUberStruct> pending_struct;
// Acquire the lock and fetch the next PendingUberStruct, if there is one.
{
std::scoped_lock lock(queue_mutex_);
if (pending_structs_.empty()) {
pending_struct = std::nullopt;
} else {
pending_struct = std::move(pending_structs_.front());
pending_structs_.pop();
}
}
return pending_struct;
}
size_t UberStructSystem::UberStructQueue::GetPendingSize() {
std::scoped_lock lock(queue_mutex_);
return pending_structs_.size();
}
} // namespace flatland