| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/351564777): Remove this and convert code to safer constructs. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #include "mojo/core/handle_table.h" |
| |
| #include <stdint.h> |
| |
| #include <limits> |
| |
| #include "base/trace_event/memory_dump_manager.h" |
| |
| namespace mojo { |
| namespace core { |
| |
| namespace { |
| |
| const char* GetNameForDispatcherType(Dispatcher::Type type) { |
| switch (type) { |
| case Dispatcher::Type::UNKNOWN: |
| return "unknown"; |
| case Dispatcher::Type::MESSAGE_PIPE: |
| return "message_pipe"; |
| case Dispatcher::Type::DATA_PIPE_PRODUCER: |
| return "data_pipe_producer"; |
| case Dispatcher::Type::DATA_PIPE_CONSUMER: |
| return "data_pipe_consumer"; |
| case Dispatcher::Type::SHARED_BUFFER: |
| return "shared_buffer"; |
| case Dispatcher::Type::WATCHER: |
| return "watcher"; |
| case Dispatcher::Type::PLATFORM_HANDLE: |
| return "platform_handle"; |
| case Dispatcher::Type::INVITATION: |
| return "invitation"; |
| } |
| NOTREACHED_IN_MIGRATION(); |
| return "unknown"; |
| } |
| |
| } // namespace |
| |
| HandleTable::EntriesAccessor::EntriesAccessor() = default; |
| |
| HandleTable::EntriesAccessor::~EntriesAccessor() = default; |
| |
| bool HandleTable::EntriesAccessor::Add(const MojoHandle handle, Entry entry) { |
| return handles_.emplace(handle, std::move(entry)).second; |
| } |
| |
| const scoped_refptr<Dispatcher>* HandleTable::EntriesAccessor::GetDispatcher( |
| const MojoHandle handle) { |
| if (last_read_handle_ != MOJO_HANDLE_INVALID && last_read_handle_ == handle) { |
| return &last_read_dispatcher_; |
| } |
| const auto iter = handles_.find(handle); |
| if (iter == handles_.end()) { |
| return nullptr; |
| } |
| last_read_handle_ = handle; |
| last_read_dispatcher_ = iter->second.dispatcher; |
| return &last_read_dispatcher_; |
| } |
| |
| HandleTable::Entry* HandleTable::EntriesAccessor::GetMutable( |
| const MojoHandle handle) { |
| const auto iter = handles_.find(handle); |
| return iter == handles_.end() ? nullptr : &iter->second; |
| } |
| |
| MojoResult HandleTable::EntriesAccessor::Remove( |
| const MojoHandle handle, |
| const HandleTable::EntriesAccessor::RemovalCondition removal_condition, |
| scoped_refptr<Dispatcher>* dispatcher) { |
| auto iter = handles_.find(handle); |
| if (iter == handles_.end()) { |
| return MOJO_RESULT_NOT_FOUND; |
| } |
| const bool is_busy = iter->second.busy; |
| const bool remove_only_if_busy = |
| removal_condition == RemovalCondition::kRemoveOnlyIfBusy; |
| if (remove_only_if_busy == is_busy) { |
| if (dispatcher != nullptr) { |
| *dispatcher = iter->second.dispatcher; |
| } |
| if (iter->first == last_read_handle_) { |
| last_read_handle_ = MOJO_HANDLE_INVALID; |
| last_read_dispatcher_.reset(); |
| } |
| handles_.erase(iter); |
| } |
| return is_busy ? MOJO_RESULT_BUSY : MOJO_RESULT_OK; |
| } |
| |
| const std::unordered_map<MojoHandle, HandleTable::Entry>& |
| HandleTable::EntriesAccessor::GetUnderlyingMap() const { |
| return handles_; |
| } |
| |
| HandleTable::HandleTable() = default; |
| |
| HandleTable::~HandleTable() = default; |
| |
| base::Lock& HandleTable::GetLock() { |
| return lock_; |
| } |
| |
| MojoHandle HandleTable::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) { |
| // Oops, we're out of handles. |
| if (next_available_handle_ == MOJO_HANDLE_INVALID) |
| return MOJO_HANDLE_INVALID; |
| |
| MojoHandle handle = next_available_handle_++; |
| const bool inserted = entries_.Add(handle, Entry(std::move(dispatcher))); |
| DCHECK(inserted); |
| |
| return handle; |
| } |
| |
| bool HandleTable::AddDispatchersFromTransit( |
| const std::vector<Dispatcher::DispatcherInTransit>& dispatchers, |
| MojoHandle* handles) { |
| // Oops, we're out of handles. |
| if (next_available_handle_ == MOJO_HANDLE_INVALID) { |
| return false; |
| } |
| |
| // MOJO_HANDLE_INVALID is zero. |
| DCHECK_GE(next_available_handle_, 1u); |
| |
| // If this insertion would cause handle overflow, we're out of handles. |
| const uintptr_t num_handles_available = |
| std::numeric_limits<uintptr_t>::max() - next_available_handle_ + 1; |
| if (num_handles_available < dispatchers.size()) { |
| return false; |
| } |
| |
| for (size_t i = 0; i < dispatchers.size(); ++i) { |
| MojoHandle handle = MOJO_HANDLE_INVALID; |
| if (dispatchers[i].dispatcher) { |
| handle = next_available_handle_++; |
| const bool inserted = |
| entries_.Add(handle, Entry(dispatchers[i].dispatcher)); |
| DCHECK(inserted); |
| } |
| handles[i] = handle; |
| } |
| |
| return true; |
| } |
| |
| scoped_refptr<Dispatcher> HandleTable::GetDispatcher(MojoHandle handle) { |
| const scoped_refptr<Dispatcher>* dispatcher = entries_.GetDispatcher(handle); |
| return dispatcher == nullptr ? nullptr : *dispatcher; |
| } |
| |
| MojoResult HandleTable::GetAndRemoveDispatcher( |
| MojoHandle handle, |
| scoped_refptr<Dispatcher>* dispatcher) { |
| scoped_refptr<Dispatcher> removed_dispatcher; |
| const MojoResult remove_result = entries_.Remove( |
| handle, EntriesAccessor::RemovalCondition::kRemoveOnlyIfNotBusy, |
| &removed_dispatcher); |
| if (remove_result == MOJO_RESULT_NOT_FOUND) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| if (remove_result == MOJO_RESULT_BUSY) { |
| return MOJO_RESULT_BUSY; |
| } |
| *dispatcher = std::move(removed_dispatcher); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult HandleTable::BeginTransit( |
| const MojoHandle* handles, |
| size_t num_handles, |
| std::vector<Dispatcher::DispatcherInTransit>* dispatchers) { |
| dispatchers->reserve(dispatchers->size() + num_handles); |
| for (size_t i = 0; i < num_handles; ++i) { |
| Entry* entry = entries_.GetMutable(handles[i]); |
| if (entry == nullptr) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| if (entry->busy) { |
| return MOJO_RESULT_BUSY; |
| } |
| |
| Dispatcher::DispatcherInTransit d; |
| d.local_handle = handles[i]; |
| d.dispatcher = entry->dispatcher; |
| if (!d.dispatcher->BeginTransit()) |
| return MOJO_RESULT_BUSY; |
| entry->busy = true; |
| dispatchers->push_back(d); |
| } |
| return MOJO_RESULT_OK; |
| } |
| |
| void HandleTable::CompleteTransitAndClose( |
| const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) { |
| for (const auto& dispatcher : dispatchers) { |
| const MojoResult remove_result = |
| entries_.Remove(dispatcher.local_handle, |
| EntriesAccessor::RemovalCondition::kRemoveOnlyIfBusy, |
| /*dispatcher=*/nullptr); |
| DCHECK(remove_result == MOJO_RESULT_BUSY); |
| dispatcher.dispatcher->CompleteTransitAndClose(); |
| } |
| } |
| |
| void HandleTable::CancelTransit( |
| const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) { |
| for (const auto& dispatcher : dispatchers) { |
| Entry* entry = entries_.GetMutable(dispatcher.local_handle); |
| DCHECK(entry != nullptr && entry->busy); |
| entry->busy = false; |
| dispatcher.dispatcher->CancelTransit(); |
| } |
| } |
| |
| void HandleTable::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) { |
| handles->clear(); |
| for (const auto& entry : entries_.GetUnderlyingMap()) |
| handles->push_back(entry.first); |
| } |
| |
| // MemoryDumpProvider implementation. |
| bool HandleTable::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, |
| base::trace_event::ProcessMemoryDump* pmd) { |
| // Create entries for all relevant dispatcher types to ensure they are present |
| // in the final dump. |
| std::map<Dispatcher::Type, int> handle_count; |
| handle_count[Dispatcher::Type::MESSAGE_PIPE]; |
| handle_count[Dispatcher::Type::DATA_PIPE_PRODUCER]; |
| handle_count[Dispatcher::Type::DATA_PIPE_CONSUMER]; |
| handle_count[Dispatcher::Type::SHARED_BUFFER]; |
| handle_count[Dispatcher::Type::WATCHER]; |
| handle_count[Dispatcher::Type::PLATFORM_HANDLE]; |
| handle_count[Dispatcher::Type::INVITATION]; |
| |
| // Count the number of each dispatcher type. |
| { |
| base::AutoLock lock(GetLock()); |
| for (const auto& entry : entries_.GetUnderlyingMap()) { |
| ++handle_count[entry.second.dispatcher->GetType()]; |
| } |
| } |
| |
| for (const auto& entry : handle_count) { |
| base::trace_event::MemoryAllocatorDump* inner_dump = |
| pmd->CreateAllocatorDump(std::string("mojo/") + |
| GetNameForDispatcherType(entry.first)); |
| inner_dump->AddScalar( |
| base::trace_event::MemoryAllocatorDump::kNameObjectCount, |
| base::trace_event::MemoryAllocatorDump::kUnitsObjects, entry.second); |
| } |
| |
| return true; |
| } |
| |
| HandleTable::Entry::Entry(scoped_refptr<Dispatcher> dispatcher) |
| : dispatcher(std::move(dispatcher)) {} |
| |
| HandleTable::Entry::~Entry() = default; |
| HandleTable::Entry::Entry(const Entry& entry) = default; |
| |
| } // namespace core |
| } // namespace mojo |