Commit f8c77740 authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Commit Bot

[CrOS MultiDevice] Add SharedResourceScheduler.

This class is used to prioritize incoming connection requests according
to their priority and time that they were received.

This CL will be used in the upcoming BleAdvertiser class, but it is
named generically so that it can be easily reused for future connection
mediums that require shared resources.

Bug: 824568, 752273
Change-Id: I9a21997f3e03cc439412c3bb8db398a5a448e146
Reviewed-on: https://chromium-review.googlesource.com/1088152Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565468}
parent 337748e4
......@@ -76,6 +76,8 @@ static_library("secure_channel") {
"secure_channel_impl.h",
"secure_channel_service.cc",
"secure_channel_service.h",
"shared_resource_scheduler.cc",
"shared_resource_scheduler.h",
"single_client_message_proxy.cc",
"single_client_message_proxy.h",
"single_client_message_proxy_impl.cc",
......@@ -172,6 +174,7 @@ source_set("unit_tests") {
"pending_connection_manager_impl_unittest.cc",
"pending_connection_request_base_unittest.cc",
"secure_channel_service_unittest.cc",
"shared_resource_scheduler_unittest.cc",
"single_client_message_proxy_impl_unittest.cc",
]
......
// 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/proximity_auth/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
// 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.
#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_SHARED_RESOURCE_SCHEDULER_H_
#define CHROMEOS_SERVICES_SECURE_CHANNEL_SHARED_RESOURCE_SCHEDULER_H_
#include <list>
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/optional.h"
#include "chromeos/services/secure_channel/device_id_pair.h"
#include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h"
namespace chromeos {
namespace secure_channel {
// Schedules requests for shared resources. When requested connections require
// using a system resouce which is limited (e.g., a limited number of Bluetooth
// advertisements can be present at one time), requests are queued up.
//
// SharedResourceScheduler returns the highest-priority request first. If two
// requests have been provided that have the same priority, the one which was
// provided to this class is returned first.
class SharedResourceScheduler {
public:
SharedResourceScheduler();
virtual ~SharedResourceScheduler();
// Schedules a request to use a shared resource.
void ScheduleRequest(const DeviceIdPair& request,
ConnectionPriority connection_priority);
// Updates a previously-scheduled request to a new priority.
void UpdateRequestPriority(const DeviceIdPair& request,
ConnectionPriority connection_priority);
// Removes a request from the scheduler.
void RemoveScheduledRequest(const DeviceIdPair& request);
// Returns the next scheduled request, or base::nullopt if there are no
// requests scheduled. Once a request is retrieved via this function, it is
// removed from the scheduler and will not be re-scheduled unless a new call
// to ScheduleRequest() is made.
base::Optional<std::pair<DeviceIdPair, ConnectionPriority>>
GetNextScheduledRequest();
// Returns the priority of the the request which will next be returned by
// GetNextScheduledRequest(). If no requests are currently scheduled,
// base::nullopt is returned.
base::Optional<ConnectionPriority> GetHighestPriorityOfScheduledRequests();
bool empty() const { return request_to_priority_map_.empty(); }
private:
// Map from priority to a list of pending requests. Each list is ordered such
// that requests that should be processed first reside before requests that
// should be processed afterward.
base::flat_map<ConnectionPriority, std::list<DeviceIdPair>>
priority_to_queued_requests_map_;
// Map from request to its priority.
base::flat_map<DeviceIdPair, ConnectionPriority> request_to_priority_map_;
DISALLOW_COPY_AND_ASSIGN(SharedResourceScheduler);
};
} // namespace secure_channel
} // namespace chromeos
#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_SHARED_RESOURCE_SCHEDULER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment