Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 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/core_impl.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "mojo/system/dispatcher.h" | |
| 11 #include "mojo/system/limits.h" | |
| 12 #include "mojo/system/memory.h" | |
| 13 #include "mojo/system/message_pipe.h" | |
| 14 #include "mojo/system/message_pipe_dispatcher.h" | |
| 15 #include "mojo/system/waiter.h" | |
| 16 | |
| 17 namespace mojo { | |
| 18 namespace system { | |
| 19 | |
| 20 // Implementation notes | |
| 21 // | |
| 22 // Mojo primitives are implemented by the singleton |CoreImpl| object. Most | |
| 23 // calls are for a "primary" handle (the first argument). | |
| 24 // |CoreImpl::GetDispatcher()| is used to look up a |Dispatcher| object for a | |
| 25 // given handle. That object implements most primitives for that object. The | |
| 26 // wait primitives are not attached to objects and are implemented by |CoreImpl| | |
| 27 // itself. | |
| 28 // | |
| 29 // Some objects have multiple handles associated to them, e.g., message pipes | |
| 30 // (which have two). In such a case, there is still a |Dispatcher| (e.g., | |
| 31 // |MessagePipeDispatcher|) for each handle, with each handle having a strong | |
| 32 // reference to the common "secondary" object (e.g., |MessagePipe|). This | |
| 33 // secondary object does NOT have any references to the |Dispatcher|s (even if | |
| 34 // it did, it wouldn't be able to do anything with them due to lock order | |
| 35 // requirements -- see below). | |
| 36 // | |
| 37 // Waiting is implemented by having the thread that wants to wait call the | |
| 38 // |Dispatcher|s for the handles that it wants to wait on with a |Waiter| | |
| 39 // object; this |Waiter| object may be created on the stack of that thread or be | |
| 40 // kept in thread local storage for that thread (TODO(vtl): future improvement). | |
| 41 // The |Dispatcher| then adds the |Waiter| to a |WaiterList| that's either owned | |
| 42 // by that |Dispatcher| (see |SimpleDispatcher|) or by a secondary object (e.g., | |
| 43 // |MessagePipe|). To signal/wake a |Waiter|, the object in question -- either a | |
| 44 // |SimpleDispatcher| or a secondary object -- talks to its |WaiterList|. | |
| 45 | |
| 46 // Thread-safety notes | |
| 47 // | |
| 48 // Mojo primitives calls are thread-safe. We achieve this with relatively | |
| 49 // fine-grained locking. There is a global handle table lock. This lock should | |
| 50 // be held as briefly as possible (TODO(vtl): a future improvement would be to | |
| 51 // switch it to a reader-writer lock). Each |Dispatcher| object then has a lock | |
| 52 // (which subclasses can use to protect their data). | |
| 53 // | |
| 54 // The lock ordering is as follows: | |
| 55 // 1. global handle table lock | |
| 56 // 2. |Dispatcher| locks | |
| 57 // 3. secondary object locks | |
| 58 // ... | |
| 59 // INF. |Waiter| locks | |
| 60 // | |
| 61 // Notes: | |
| 62 // - While holding a |Dispatcher| lock, you may not unconditionally attempt | |
| 63 // to take another |Dispatcher| lock. (This has consequences on the | |
| 64 // concurrency semantics of |MojoWriteMessage()| when passing handles.) | |
| 65 // Doing so would lead to deadlock. | |
| 66 // - Locks at the "INF" level may not have any locks taken while they are | |
| 67 // held. | |
| 68 | |
| 69 // static | |
| 70 CoreImpl* CoreImpl::singleton_ = NULL; | |
| 71 | |
| 72 // static | |
| 73 void CoreImpl::Init() { | |
| 74 CHECK(!singleton_); | |
| 75 singleton_ = new CoreImpl(); | |
| 76 } | |
| 77 | |
| 78 MojoResult CoreImpl::Close(MojoHandle handle) { | |
| 79 if (handle == MOJO_HANDLE_INVALID) | |
| 80 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 81 | |
| 82 scoped_refptr<Dispatcher> dispatcher; | |
| 83 { | |
| 84 base::AutoLock locker(handle_table_lock_); | |
| 85 HandleTableMap::iterator it = handle_table_.find(handle); | |
| 86 if (it == handle_table_.end()) | |
| 87 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 88 dispatcher = it->second; | |
| 89 handle_table_.erase(it); | |
| 90 } | |
| 91 | |
| 92 // The dispatcher doesn't have a say in being closed, but gets notified of it. | |
| 93 // Note: This is done outside of |handle_table_lock_|. As a result, there's a | |
| 94 // race condition that the dispatcher must handle; see the comment in | |
| 95 // |Dispatcher| in dispatcher.h. | |
| 96 return dispatcher->Close(); | |
| 97 } | |
| 98 | |
| 99 MojoResult CoreImpl::Wait(MojoHandle handle, | |
| 100 MojoWaitFlags flags, | |
| 101 MojoDeadline deadline) { | |
| 102 return WaitManyInternal(&handle, &flags, 1, deadline); | |
| 103 } | |
| 104 | |
| 105 MojoResult CoreImpl::WaitMany(const MojoHandle* handles, | |
| 106 const MojoWaitFlags* flags, | |
| 107 uint32_t num_handles, | |
| 108 MojoDeadline deadline) { | |
| 109 if (!VerifyUserPointer(handles, num_handles, sizeof(handles[0]))) | |
| 110 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 111 if (!VerifyUserPointer(flags, num_handles, sizeof(flags[0]))) | |
| 112 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 113 if (num_handles < 1) | |
| 114 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 115 if (num_handles > kMaxWaitManyNumHandles) | |
| 116 return MOJO_RESULT_RESOURCE_EXHAUSTED; | |
| 117 return WaitManyInternal(handles, flags, num_handles, deadline); | |
| 118 } | |
| 119 | |
| 120 MojoResult CoreImpl::CreateMessagePipe(MojoHandle* handle_0, | |
| 121 MojoHandle* handle_1) { | |
| 122 scoped_refptr<MessagePipeDispatcher> dispatcher_0( | |
| 123 new MessagePipeDispatcher()); | |
| 124 scoped_refptr<MessagePipeDispatcher> dispatcher_1( | |
| 125 new MessagePipeDispatcher()); | |
| 126 | |
| 127 MojoHandle h0, h1; | |
| 128 { | |
| 129 base::AutoLock locker(handle_table_lock_); | |
| 130 | |
| 131 h0 = AddDispatcherNoLock(dispatcher_0); | |
| 132 if (h0 == MOJO_HANDLE_INVALID) | |
| 133 return MOJO_RESULT_RESOURCE_EXHAUSTED; | |
| 134 | |
| 135 h1 = AddDispatcherNoLock(dispatcher_1); | |
| 136 if (h1 == MOJO_HANDLE_INVALID) { | |
| 137 handle_table_.erase(h0); | |
| 138 return MOJO_RESULT_RESOURCE_EXHAUSTED; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 scoped_refptr<MessagePipe> message_pipe(new MessagePipe()); | |
| 143 dispatcher_0->Init(message_pipe, 0); | |
| 144 dispatcher_1->Init(message_pipe, 1); | |
| 145 | |
| 146 *handle_0 = h0; | |
| 147 *handle_1 = h1; | |
| 148 return MOJO_RESULT_OK; | |
| 149 } | |
| 150 | |
| 151 MojoResult CoreImpl::WriteMessage( | |
| 152 MojoHandle handle, | |
| 153 const void* bytes, uint32_t num_bytes, | |
| 154 const MojoHandle* handles, uint32_t num_handles, | |
| 155 MojoWriteMessageFlags flags) { | |
| 156 scoped_refptr<Dispatcher> dispatcher(GetDispatcher(handle)); | |
| 157 if (!dispatcher.get()) | |
| 158 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 159 | |
| 160 return dispatcher->WriteMessage(bytes, num_bytes, | |
| 161 handles, num_handles, | |
| 162 flags); | |
| 163 } | |
| 164 | |
| 165 MojoResult CoreImpl::ReadMessage( | |
| 166 MojoHandle handle, | |
| 167 void* bytes, uint32_t* num_bytes, | |
| 168 MojoHandle* handles, uint32_t* num_handles, | |
| 169 MojoReadMessageFlags flags) { | |
| 170 scoped_refptr<Dispatcher> dispatcher(GetDispatcher(handle)); | |
| 171 if (!dispatcher.get()) | |
| 172 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 173 | |
| 174 return dispatcher->ReadMessage(bytes, num_bytes, | |
| 175 handles, num_handles, | |
| 176 flags); | |
| 177 } | |
| 178 | |
| 179 CoreImpl::CoreImpl() | |
| 180 : next_handle_(MOJO_HANDLE_INVALID + 1) { | |
| 181 } | |
| 182 | |
| 183 CoreImpl::~CoreImpl() { | |
| 184 // This should usually not be reached (the singleton lives forever), except | |
| 185 // in tests. | |
| 186 } | |
| 187 | |
| 188 scoped_refptr<Dispatcher> CoreImpl::GetDispatcher(MojoHandle handle) { | |
| 189 if (handle == MOJO_HANDLE_INVALID) | |
| 190 return NULL; | |
| 191 | |
| 192 base::AutoLock locker(handle_table_lock_); | |
| 193 HandleTableMap::iterator it = handle_table_.find(handle); | |
| 194 if (it == handle_table_.end()) | |
| 195 return NULL; | |
| 196 | |
| 197 return it->second; | |
| 198 } | |
| 199 | |
| 200 MojoHandle CoreImpl::AddDispatcherNoLock(scoped_refptr<Dispatcher> dispatcher) { | |
| 201 DCHECK(dispatcher.get()); | |
| 202 handle_table_lock_.AssertAcquired(); | |
| 203 DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID); | |
| 204 | |
| 205 if (handle_table_.size() >= kMaxHandleTableSize) | |
| 206 return MOJO_HANDLE_INVALID; | |
| 207 | |
| 208 // TODO(vtl): Maybe we want to do something different/smarter. (Or maybe try | |
| 209 // assigning randomly?) | |
| 210 while (handle_table_.find(next_handle_) != handle_table_.end()) { | |
| 211 next_handle_++; | |
| 212 if (next_handle_ == MOJO_HANDLE_INVALID) | |
| 213 next_handle_++; | |
| 214 } | |
| 215 | |
| 216 MojoHandle new_handle = next_handle_; | |
| 217 handle_table_[new_handle] = dispatcher; | |
| 218 | |
| 219 next_handle_++; | |
| 220 if (next_handle_ == MOJO_HANDLE_INVALID) | |
| 221 next_handle_++; | |
| 222 | |
| 223 return new_handle; | |
| 224 } | |
| 225 | |
| 226 MojoResult CoreImpl::WaitManyInternal(const MojoHandle* handles, | |
| 227 const MojoWaitFlags* flags, | |
| 228 uint32_t num_handles, | |
| 229 MojoDeadline deadline) { | |
| 230 DCHECK_GT(num_handles, 0u); | |
| 231 | |
| 232 std::vector<scoped_refptr<Dispatcher> > dispatchers; | |
| 233 dispatchers.reserve(num_handles); | |
| 234 for (uint32_t i = 0; i < num_handles; i++) { | |
| 235 scoped_refptr<Dispatcher> d = GetDispatcher(handles[i]); | |
| 236 if (!d.get()) | |
| 237 return MOJO_RESULT_INVALID_ARGUMENT; | |
| 238 dispatchers.push_back(d); | |
|
darin (slow to review)
2013/09/27 15:34:47
Do we want to check for the same handle being spec
viettrungluu
2013/09/27 17:58:01
It's intended to be resilient to that case, and th
| |
| 239 } | |
| 240 | |
| 241 // TODO(vtl): Should make the waiter live (permanently) in TLS. | |
| 242 Waiter waiter; | |
| 243 waiter.Init(); | |
| 244 | |
| 245 uint32_t i; | |
| 246 MojoResult rv = MOJO_RESULT_OK; | |
| 247 for (i = 0; i < num_handles; i++) { | |
| 248 rv = dispatchers[i]->AddWaiter(&waiter, | |
| 249 flags[i], | |
| 250 static_cast<MojoResult>(i)); | |
| 251 if (rv != MOJO_RESULT_OK) | |
| 252 break; | |
| 253 } | |
| 254 uint32_t num_added = i; | |
| 255 | |
| 256 if (rv == MOJO_RESULT_ALREADY_EXISTS) | |
| 257 rv = static_cast<MojoResult>(i); // The i-th one is already "triggered". | |
| 258 else if (rv == MOJO_RESULT_OK) | |
| 259 rv = waiter.Wait(deadline); | |
| 260 | |
| 261 for (i = 0; i < num_added; i++) | |
| 262 dispatchers[i]->RemoveWaiter(&waiter); | |
|
viettrungluu
2013/09/27 17:58:01
I'm not sure if I was smart when I wrote this or j
| |
| 263 | |
| 264 return rv; | |
| 265 } | |
| 266 | |
| 267 } // namespace system | |
| 268 } // namespace mojo | |
| OLD | NEW |