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