1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "mojo/system/handle_table.h" 6 7#include "base/logging.h" 8#include "base/macros.h" 9#include "mojo/system/constants.h" 10#include "mojo/system/dispatcher.h" 11 12namespace mojo { 13namespace system { 14 15HandleTable::Entry::Entry() : busy(false) { 16} 17 18HandleTable::Entry::Entry(const scoped_refptr<Dispatcher>& dispatcher) 19 : dispatcher(dispatcher), busy(false) { 20} 21 22HandleTable::Entry::~Entry() { 23 DCHECK(!busy); 24} 25 26HandleTable::HandleTable() : next_handle_(MOJO_HANDLE_INVALID + 1) { 27} 28 29HandleTable::~HandleTable() { 30 // This should usually not be reached (the only instance should be owned by 31 // the singleton |Core|, which lives forever), except in tests. 32} 33 34Dispatcher* HandleTable::GetDispatcher(MojoHandle handle) { 35 DCHECK_NE(handle, MOJO_HANDLE_INVALID); 36 37 HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle); 38 if (it == handle_to_entry_map_.end()) 39 return nullptr; 40 return it->second.dispatcher.get(); 41} 42 43MojoResult HandleTable::GetAndRemoveDispatcher( 44 MojoHandle handle, 45 scoped_refptr<Dispatcher>* dispatcher) { 46 DCHECK_NE(handle, MOJO_HANDLE_INVALID); 47 DCHECK(dispatcher); 48 49 HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle); 50 if (it == handle_to_entry_map_.end()) 51 return MOJO_RESULT_INVALID_ARGUMENT; 52 if (it->second.busy) 53 return MOJO_RESULT_BUSY; 54 *dispatcher = it->second.dispatcher; 55 handle_to_entry_map_.erase(it); 56 57 return MOJO_RESULT_OK; 58} 59 60MojoHandle HandleTable::AddDispatcher( 61 const scoped_refptr<Dispatcher>& dispatcher) { 62 if (handle_to_entry_map_.size() >= kMaxHandleTableSize) 63 return MOJO_HANDLE_INVALID; 64 return AddDispatcherNoSizeCheck(dispatcher); 65} 66 67std::pair<MojoHandle, MojoHandle> HandleTable::AddDispatcherPair( 68 const scoped_refptr<Dispatcher>& dispatcher0, 69 const scoped_refptr<Dispatcher>& dispatcher1) { 70 if (handle_to_entry_map_.size() + 1 >= kMaxHandleTableSize) 71 return std::make_pair(MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID); 72 return std::make_pair(AddDispatcherNoSizeCheck(dispatcher0), 73 AddDispatcherNoSizeCheck(dispatcher1)); 74} 75 76bool HandleTable::AddDispatcherVector(const DispatcherVector& dispatchers, 77 MojoHandle* handles) { 78 DCHECK_LE(dispatchers.size(), kMaxMessageNumHandles); 79 DCHECK(handles); 80 // TODO(vtl): |std::numeric_limits<size_t>::max()| isn't a compile-time 81 // expression in C++03. 82 static_assert( 83 static_cast<uint64_t>(kMaxHandleTableSize) + kMaxMessageNumHandles < 84 (sizeof(size_t) == 8 ? kuint64max 85 : static_cast<uint64_t>(kuint32max)), 86 "Addition may overflow"); 87 88 if (handle_to_entry_map_.size() + dispatchers.size() > kMaxHandleTableSize) 89 return false; 90 91 for (size_t i = 0; i < dispatchers.size(); i++) { 92 if (dispatchers[i].get()) { 93 handles[i] = AddDispatcherNoSizeCheck(dispatchers[i]); 94 } else { 95 LOG(WARNING) << "Invalid dispatcher at index " << i; 96 handles[i] = MOJO_HANDLE_INVALID; 97 } 98 } 99 return true; 100} 101 102MojoResult HandleTable::MarkBusyAndStartTransport( 103 MojoHandle disallowed_handle, 104 const MojoHandle* handles, 105 uint32_t num_handles, 106 std::vector<DispatcherTransport>* transports) { 107 DCHECK_NE(disallowed_handle, MOJO_HANDLE_INVALID); 108 DCHECK(handles); 109 DCHECK_LE(num_handles, kMaxMessageNumHandles); 110 DCHECK(transports); 111 DCHECK_EQ(transports->size(), num_handles); 112 113 std::vector<Entry*> entries(num_handles); 114 115 // First verify all the handles and get their dispatchers. 116 uint32_t i; 117 MojoResult error_result = MOJO_RESULT_INTERNAL; 118 for (i = 0; i < num_handles; i++) { 119 // Sending your own handle is not allowed (and, for consistency, returns 120 // "busy"). 121 if (handles[i] == disallowed_handle) { 122 error_result = MOJO_RESULT_BUSY; 123 break; 124 } 125 126 HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]); 127 if (it == handle_to_entry_map_.end()) { 128 error_result = MOJO_RESULT_INVALID_ARGUMENT; 129 break; 130 } 131 132 entries[i] = &it->second; 133 if (entries[i]->busy) { 134 error_result = MOJO_RESULT_BUSY; 135 break; 136 } 137 // Note: By marking the handle as busy here, we're also preventing the 138 // same handle from being sent multiple times in the same message. 139 entries[i]->busy = true; 140 141 // Try to start the transport. 142 DispatcherTransport transport = 143 Dispatcher::HandleTableAccess::TryStartTransport( 144 entries[i]->dispatcher.get()); 145 if (!transport.is_valid()) { 146 // Only log for Debug builds, since this is not a problem with the system 147 // code, but with user code. 148 DLOG(WARNING) << "Likely race condition in user code detected: attempt " 149 "to transfer handle " << handles[i] 150 << " while it is in use on a different thread"; 151 152 // Unset the busy flag (since it won't be unset below). 153 entries[i]->busy = false; 154 error_result = MOJO_RESULT_BUSY; 155 break; 156 } 157 158 // Check if the dispatcher is busy (e.g., in a two-phase read/write). 159 // (Note that this must be done after the dispatcher's lock is acquired.) 160 if (transport.IsBusy()) { 161 // Unset the busy flag and end the transport (since it won't be done 162 // below). 163 entries[i]->busy = false; 164 transport.End(); 165 error_result = MOJO_RESULT_BUSY; 166 break; 167 } 168 169 // Hang on to the transport (which we'll need to end the transport). 170 (*transports)[i] = transport; 171 } 172 if (i < num_handles) { 173 DCHECK_NE(error_result, MOJO_RESULT_INTERNAL); 174 175 // Unset the busy flags and release the locks. 176 for (uint32_t j = 0; j < i; j++) { 177 DCHECK(entries[j]->busy); 178 entries[j]->busy = false; 179 (*transports)[j].End(); 180 } 181 return error_result; 182 } 183 184 return MOJO_RESULT_OK; 185} 186 187MojoHandle HandleTable::AddDispatcherNoSizeCheck( 188 const scoped_refptr<Dispatcher>& dispatcher) { 189 DCHECK(dispatcher.get()); 190 DCHECK_LT(handle_to_entry_map_.size(), kMaxHandleTableSize); 191 DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID); 192 193 // TODO(vtl): Maybe we want to do something different/smarter. (Or maybe try 194 // assigning randomly?) 195 while (handle_to_entry_map_.find(next_handle_) != 196 handle_to_entry_map_.end()) { 197 next_handle_++; 198 if (next_handle_ == MOJO_HANDLE_INVALID) 199 next_handle_++; 200 } 201 202 MojoHandle new_handle = next_handle_; 203 handle_to_entry_map_[new_handle] = Entry(dispatcher); 204 205 next_handle_++; 206 if (next_handle_ == MOJO_HANDLE_INVALID) 207 next_handle_++; 208 209 return new_handle; 210} 211 212void HandleTable::RemoveBusyHandles(const MojoHandle* handles, 213 uint32_t num_handles) { 214 DCHECK(handles); 215 DCHECK_LE(num_handles, kMaxMessageNumHandles); 216 217 for (uint32_t i = 0; i < num_handles; i++) { 218 HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]); 219 DCHECK(it != handle_to_entry_map_.end()); 220 DCHECK(it->second.busy); 221 it->second.busy = false; // For the sake of a |DCHECK()|. 222 handle_to_entry_map_.erase(it); 223 } 224} 225 226void HandleTable::RestoreBusyHandles(const MojoHandle* handles, 227 uint32_t num_handles) { 228 DCHECK(handles); 229 DCHECK_LE(num_handles, kMaxMessageNumHandles); 230 231 for (uint32_t i = 0; i < num_handles; i++) { 232 HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]); 233 DCHECK(it != handle_to_entry_map_.end()); 234 DCHECK(it->second.busy); 235 it->second.busy = false; 236 } 237} 238 239} // namespace system 240} // namespace mojo 241