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