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