blob: 0fb8ac8d0c3672a83d77f9a7086c559e0380baa2 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "chromeos/services/secure_channel/shared_resource_scheduler.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "chromeos/components/multidevice/logging/logging.h"
namespace chromeos {
namespace secure_channel {
namespace {
// Sorted from highest priority to lowest, to ensure that high-priority
// requests are retrieved from the scheduler first.
constexpr const ConnectionPriority kOrderedPriorities[] = {
ConnectionPriority::kHigh, ConnectionPriority::kMedium,
ConnectionPriority::kLow};
// Removes |item| from |list|; emits a crash if not in the list.
void RemoveItemFromList(const DeviceIdPair& item,
std::list<DeviceIdPair>* list) {
for (auto it = list->begin(); it != list->end(); ++it) {
if (*it != item)
continue;
list->erase(it);
return;
}
PA_LOG(ERROR) << "RemoveItemFromList(): Tried to remove an item from |list|, "
<< "but that item was not present. Item: " << item;
NOTREACHED();
}
// Remove the first item from |list| and returns it. If |list| is empty,
// base::nullopt is returned.
base::Optional<DeviceIdPair> RemoveFirstItemFromList(
std::list<DeviceIdPair>* list) {
if (list->empty())
return base::nullopt;
DeviceIdPair first_item = list->front();
list->pop_front();
return first_item;
}
} // namespace
SharedResourceScheduler::SharedResourceScheduler() = default;
SharedResourceScheduler::~SharedResourceScheduler() = default;
void SharedResourceScheduler::ScheduleRequest(
const DeviceIdPair& request,
ConnectionPriority connection_priority) {
if (base::ContainsKey(request_to_priority_map_, request)) {
PA_LOG(ERROR) << "SharedResourceScheduler::ScheduleRequest(): Tried to "
<< "schedule a request which was already scheduled. Request: "
<< request << ", Priority: " << connection_priority;
NOTREACHED();
}
priority_to_queued_requests_map_[connection_priority].push_back(request);
request_to_priority_map_[request] = connection_priority;
}
void SharedResourceScheduler::UpdateRequestPriority(
const DeviceIdPair& request,
ConnectionPriority connection_priority) {
if (!base::ContainsKey(request_to_priority_map_, request)) {
PA_LOG(ERROR) << "SharedResourceScheduler::UpdateRequestPriority(): Tried "
<< "to update priority for a request which was not "
<< "scheduled. Request: " << request
<< ", Priority: " << connection_priority;
NOTREACHED();
}
if (request_to_priority_map_[request] == connection_priority) {
PA_LOG(WARNING) << "SharedResourceScheduler::UpdateRequestPriority(): "
<< "Tried update priority for a request, but the request "
<< "was already at that priority.";
return;
}
// Remove the item from the old list.
RemoveItemFromList(
request,
&priority_to_queued_requests_map_[request_to_priority_map_[request]]);
// Add it to the new list.
priority_to_queued_requests_map_[connection_priority].push_back(request);
// Update the priority map.
request_to_priority_map_[request] = connection_priority;
}
void SharedResourceScheduler::RemoveScheduledRequest(
const DeviceIdPair& request) {
if (!base::ContainsKey(request_to_priority_map_, request)) {
PA_LOG(ERROR) << "SharedResourceScheduler::RemoveScheduledRequest(): Tried "
<< "to remove a scheduled request, but that request was not "
<< "actually scheduled. Request: " << request;
NOTREACHED();
}
auto& list_for_priority =
priority_to_queued_requests_map_[request_to_priority_map_[request]];
// Remove from list in |priority_to_queued_requests_map_|.
bool was_removed_from_list = false;
for (auto it = list_for_priority.begin(); it != list_for_priority.end();
++it) {
if (*it == request) {
list_for_priority.erase(it);
was_removed_from_list = true;
break;
}
}
if (!was_removed_from_list) {
PA_LOG(ERROR) << "SharedResourceScheduler::RemoveScheduledRequest(): Tried "
<< "to remove a scheduled request, but that request was not "
<< "present in priority_to_queued_requests_map_. "
<< "Request: " << request;
NOTREACHED();
}
// Remove from |request_to_priority_map_|.
size_t num_removed = request_to_priority_map_.erase(request);
if (num_removed != 1u) {
PA_LOG(ERROR) << "SharedResourceScheduler::RemoveScheduledRequest(): Tried "
<< "to remove a scheduled request, but that request was not "
<< "present in request_to_priority_map_. "
<< "Request: " << request;
NOTREACHED();
}
}
base::Optional<std::pair<DeviceIdPair, ConnectionPriority>>
SharedResourceScheduler::GetNextScheduledRequest() {
for (const auto& priority : kOrderedPriorities) {
base::Optional<DeviceIdPair> potential_request =
RemoveFirstItemFromList(&priority_to_queued_requests_map_[priority]);
if (!potential_request)
continue;
size_t num_removed = request_to_priority_map_.erase(*potential_request);
if (num_removed != 1u) {
PA_LOG(ERROR) << "SharedResourceScheduler::GetNextScheduledRequest(): "
<< "Tried to remove request from "
<< "request_to_priority_map_, but no request was present."
<< "Request: " << *potential_request;
NOTREACHED();
}
return std::make_pair(*potential_request, priority);
}
return base::nullopt;
}
base::Optional<ConnectionPriority>
SharedResourceScheduler::GetHighestPriorityOfScheduledRequests() {
for (const auto& priority : kOrderedPriorities) {
if (!priority_to_queued_requests_map_[priority].empty())
return priority;
}
return base::nullopt;
}
} // namespace secure_channel
} // namespace chromeos