blob: 105382b4c0f7d5a7dd68c217ee589bed2a7f99bf [file]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_transfer/handler_allocator.h"
#include <mutex>
namespace pw::transfer {
bool TransferHandlerAllocator::IsIdInUse(uint32_t resource_id) {
for (const auto& entry : active_handlers_) {
if (entry.resource_id == resource_id) {
return true;
}
}
return false;
}
template <typename HandlerType, typename... Args>
Result<TransferResource> TransferHandlerAllocator::AllocateHandler(
Args&... args) {
std::lock_guard lock(lock_);
std::optional<uint32_t> resource_id;
// Search for an available resource ID. This is not optimized, and expects
// that the number of handlers is small.
uint32_t search_start = last_resource_id_ + 1;
if (search_start > max_resource_id_ || search_start < min_resource_id_) {
search_start = min_resource_id_;
}
// Loop from search_start to max_resource_id_.
for (uint32_t id = search_start; id <= max_resource_id_; ++id) {
if (id == 0) {
continue;
}
if (!IsIdInUse(id)) {
resource_id = id;
break;
}
}
if (!resource_id.has_value()) {
// Loop from min_resource_id_ to search_start.
for (uint32_t id = min_resource_id_; id < search_start; ++id) {
if (id == 0) {
continue;
}
if (!IsIdInUse(id)) {
resource_id = id;
break;
}
}
}
if (!resource_id.has_value()) {
return Status::ResourceExhausted();
}
auto handler = allocator_.MakeUnique<HandlerType>(*resource_id, args...);
if (handler == nullptr) {
return Status::ResourceExhausted();
}
if (!active_handlers_.try_emplace_back(HandlerEntry{
.resource_id = *resource_id,
.handler = std::move(handler),
})) {
return Status::ResourceExhausted();
}
Handler& handler_ref = *active_handlers_.back().handler.get();
if (!transfer_service_.RegisterHandler(handler_ref)) {
active_handlers_.pop_back();
return Status::FailedPrecondition();
}
last_resource_id_ = *resource_id;
return TransferResource(*this, *resource_id);
}
Result<TransferResource> TransferHandlerAllocator::AllocateReader(
stream::Reader& reader) {
return AllocateHandler<ReadOnlyHandler>(reader);
}
Result<TransferResource> TransferHandlerAllocator::AllocateWriter(
stream::Writer& writer) {
return AllocateHandler<WriteOnlyHandler>(writer);
}
Result<TransferResource> TransferHandlerAllocator::AllocateReadWriter(
stream::ReaderWriter& reader_writer) {
return AllocateHandler<ReadWriteHandler>(reader_writer);
}
void TransferHandlerAllocator::Close(uint32_t resource_id) {
std::lock_guard lock(lock_);
auto to_remove = std::find_if(active_handlers_.begin(),
active_handlers_.end(),
[resource_id](const HandlerEntry& entry) {
return entry.resource_id == resource_id;
});
if (to_remove == active_handlers_.end()) {
return;
}
transfer_service_.UnregisterHandler(*to_remove->handler.get());
active_handlers_.erase(to_remove);
}
void TransferResource::Close() {
if (allocator_ != nullptr && resource_id_ != 0) {
allocator_->Close(resource_id_);
}
resource_id_ = 0;
}
} // namespace pw::transfer