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