| Index: mojo/system/core_impl.cc
|
| diff --git a/mojo/system/core_impl.cc b/mojo/system/core_impl.cc
|
| index 104a373f2f2f04eec9ad9bf5cc647f39aadc8f19..70a8e2b3ab72c6327d4c7045644f55daba6c23d5 100644
|
| --- a/mojo/system/core_impl.cc
|
| +++ b/mojo/system/core_impl.cc
|
| @@ -66,6 +66,20 @@ namespace system {
|
| // - Locks at the "INF" level may not have any locks taken while they are
|
| // held.
|
|
|
| +CoreImpl::HandleTableEntry::HandleTableEntry()
|
| + : busy(false) {
|
| +}
|
| +
|
| +CoreImpl::HandleTableEntry::HandleTableEntry(
|
| + const scoped_refptr<Dispatcher>& dispatcher)
|
| + : dispatcher(dispatcher),
|
| + busy(false) {
|
| +}
|
| +
|
| +CoreImpl::HandleTableEntry::~HandleTableEntry() {
|
| + DCHECK(!busy);
|
| +}
|
| +
|
| // static
|
| CoreImpl* CoreImpl::singleton_ = NULL;
|
|
|
| @@ -85,7 +99,9 @@ MojoResult CoreImpl::Close(MojoHandle handle) {
|
| HandleTableMap::iterator it = handle_table_.find(handle);
|
| if (it == handle_table_.end())
|
| return MOJO_RESULT_INVALID_ARGUMENT;
|
| - dispatcher = it->second;
|
| + if (it->second.busy)
|
| + return MOJO_RESULT_BUSY;
|
| + dispatcher = it->second.dispatcher;
|
| handle_table_.erase(it);
|
| }
|
|
|
| @@ -119,6 +135,11 @@ MojoResult CoreImpl::WaitMany(const MojoHandle* handles,
|
|
|
| MojoResult CoreImpl::CreateMessagePipe(MojoHandle* handle_0,
|
| MojoHandle* handle_1) {
|
| + if (!VerifyUserPointer<MojoHandle>(handle_0, 1))
|
| + return MOJO_RESULT_INVALID_ARGUMENT;
|
| + if (!VerifyUserPointer<MojoHandle>(handle_1, 1))
|
| + return MOJO_RESULT_INVALID_ARGUMENT;
|
| +
|
| scoped_refptr<MessagePipeDispatcher> dispatcher_0(
|
| new MessagePipeDispatcher());
|
| scoped_refptr<MessagePipeDispatcher> dispatcher_1(
|
| @@ -157,9 +178,124 @@ MojoResult CoreImpl::WriteMessage(
|
| if (!dispatcher.get())
|
| return MOJO_RESULT_INVALID_ARGUMENT;
|
|
|
| - return dispatcher->WriteMessage(bytes, num_bytes,
|
| - handles, num_handles,
|
| - flags);
|
| + // Easy case: not sending any handles.
|
| + if (num_handles == 0)
|
| + return dispatcher->WriteMessage(bytes, num_bytes, NULL, flags);
|
| +
|
| + // We have to handle |handles| here, since we have to mark them busy in the
|
| + // global handle table. We can't delegate this to the dispatcher, since the
|
| + // handle table lock must be acquired before the dispatcher lock.
|
| + //
|
| + // (This leads to an oddity: |handles|/|num_handles| are always verified for
|
| + // validity, even for dispatchers that don't support |WriteMessage()| and will
|
| + // simply return failure unconditionally. It also breaks the usual
|
| + // left-to-right verification order of arguments.)
|
| + if (!VerifyUserPointer<MojoHandle>(handles, num_handles))
|
| + return MOJO_RESULT_INVALID_ARGUMENT;
|
| + if (num_handles > kMaxMessageNumHandles)
|
| + return MOJO_RESULT_RESOURCE_EXHAUSTED;
|
| +
|
| + // We'll need to hold on to the dispatchers so that we can pass them on to
|
| + // |WriteMessage()| and also so that we can unlock their locks afterwards
|
| + // without accessing the handle table. These can be dumb pointers, since their
|
| + // entries in the handle table won't get removed (since they'll be marked as
|
| + // busy).
|
| + std::vector<Dispatcher*> dispatchers(num_handles);
|
| +
|
| + // When we pass handles, we have to try to take all their dispatchers' locks
|
| + // and mark the handles as busy. If the call succeeds, we then remove the
|
| + // handles from the handle table.
|
| + {
|
| + base::AutoLock locker(handle_table_lock_);
|
| +
|
| + std::vector<HandleTableEntry*> entries(num_handles);
|
| +
|
| + // First verify all the handles and get their dispatchers.
|
| + uint32_t i;
|
| + MojoResult error_result = MOJO_RESULT_INTERNAL;
|
| + for (i = 0; i < num_handles; i++) {
|
| + // Sending your own handle is not allowed (and, for consistency, returns
|
| + // "busy").
|
| + if (handles[i] == handle) {
|
| + error_result = MOJO_RESULT_BUSY;
|
| + break;
|
| + }
|
| +
|
| + HandleTableMap::iterator it = handle_table_.find(handles[i]);
|
| + if (it == handle_table_.end()) {
|
| + error_result = MOJO_RESULT_INVALID_ARGUMENT;
|
| + break;
|
| + }
|
| +
|
| + entries[i] = &it->second;
|
| + if (entries[i]->busy) {
|
| + error_result = MOJO_RESULT_BUSY;
|
| + break;
|
| + }
|
| + // Note: By marking the handle as busy here, we're also preventing the
|
| + // same handle from being sent multiple times in the same message.
|
| + entries[i]->busy = true;
|
| +
|
| + // Try to take the lock.
|
| + if (!entries[i]->dispatcher->lock().Try()) {
|
| + // Unset the busy flag (since it won't be unset below).
|
| + entries[i]->busy = false;
|
| + error_result = MOJO_RESULT_BUSY;
|
| + break;
|
| + }
|
| +
|
| + // Hang on to the pointer to the dispatcher (which we'll need to release
|
| + // the lock without going through the handle table).
|
| + dispatchers[i] = entries[i]->dispatcher;
|
| + }
|
| + if (i < num_handles) {
|
| + DCHECK_NE(error_result, MOJO_RESULT_INTERNAL);
|
| +
|
| + // Unset the busy flags and release the locks.
|
| + for (uint32_t j = 0; j < i; j++) {
|
| + DCHECK(entries[j]->busy);
|
| + entries[j]->busy = false;
|
| + entries[j]->dispatcher->lock().Release();
|
| + }
|
| + return error_result;
|
| + }
|
| + }
|
| +
|
| + MojoResult rv = dispatcher->WriteMessage(bytes, num_bytes,
|
| + &dispatchers,
|
| + flags);
|
| +
|
| + // We need to release the dispatcher locks before we take the handle table
|
| + // lock.
|
| + for (uint32_t i = 0; i < num_handles; i++) {
|
| + dispatchers[i]->lock().AssertAcquired();
|
| + dispatchers[i]->lock().Release();
|
| + }
|
| +
|
| + if (rv == MOJO_RESULT_OK) {
|
| + base::AutoLock locker(handle_table_lock_);
|
| +
|
| + // Succeeded, so the handles should be removed from the handle table. (The
|
| + // transferring to new dispatchers/closing must have already been done.)
|
| + for (uint32_t i = 0; i < num_handles; i++) {
|
| + HandleTableMap::iterator it = handle_table_.find(handles[i]);
|
| + DCHECK(it != handle_table_.end());
|
| + DCHECK(it->second.busy);
|
| + handle_table_.erase(it);
|
| + }
|
| + } else {
|
| + base::AutoLock locker(handle_table_lock_);
|
| +
|
| + // Failed, so the handles should go back to their normal state.
|
| + for (uint32_t i = 0; i < num_handles; i++) {
|
| + HandleTableMap::iterator it = handle_table_.find(handles[i]);
|
| + DCHECK(it != handle_table_.end());
|
| + DCHECK(it->second.busy);
|
| + it->second.busy = false;
|
| + }
|
| + }
|
| +
|
| + return rv;
|
| }
|
|
|
| MojoResult CoreImpl::ReadMessage(
|
| @@ -171,9 +307,40 @@ MojoResult CoreImpl::ReadMessage(
|
| if (!dispatcher.get())
|
| return MOJO_RESULT_INVALID_ARGUMENT;
|
|
|
| - return dispatcher->ReadMessage(bytes, num_bytes,
|
| - handles, num_handles,
|
| - flags);
|
| + uint32_t max_num_dispatchers = 0;
|
| + if (num_handles) {
|
| + if (!VerifyUserPointer<uint32_t>(num_handles, 1))
|
| + return MOJO_RESULT_INVALID_ARGUMENT;
|
| + if (!VerifyUserPointer<MojoHandle>(handles, *num_handles))
|
| + return MOJO_RESULT_INVALID_ARGUMENT;
|
| + max_num_dispatchers = *num_handles;
|
| + }
|
| +
|
| + // Easy case: won't receive any handles.
|
| + if (max_num_dispatchers == 0)
|
| + return dispatcher->ReadMessage(bytes, num_bytes, 0, NULL, flags);
|
| +
|
| + std::vector<scoped_refptr<Dispatcher> > dispatchers;
|
| + MojoResult rv = dispatcher->ReadMessage(bytes, num_bytes,
|
| + max_num_dispatchers, &dispatchers,
|
| + flags);
|
| + if (!dispatchers.empty()) {
|
| + DCHECK_EQ(rv, MOJO_RESULT_OK);
|
| +
|
| + *num_handles = static_cast<uint32_t>(dispatchers.size());
|
| + DCHECK_LE(*num_handles, max_num_dispatchers);
|
| +
|
| + base::AutoLock locker(handle_table_lock_);
|
| +
|
| + for (size_t i = 0; i < dispatchers.size(); i++) {
|
| + // TODO(vtl): What should we do if we hit the maximum handle table size
|
| + // here? Currently, we'll just fill in those handles with
|
| + // |MOJO_HANDLE_INVALID| (and return success anyway).
|
| + handles[i] = AddDispatcherNoLock(dispatchers[i]);
|
| + }
|
| + }
|
| +
|
| + return rv;
|
| }
|
|
|
| CoreImpl::CoreImpl()
|
| @@ -194,10 +361,11 @@ scoped_refptr<Dispatcher> CoreImpl::GetDispatcher(MojoHandle handle) {
|
| if (it == handle_table_.end())
|
| return NULL;
|
|
|
| - return it->second;
|
| + return it->second.dispatcher;
|
| }
|
|
|
| -MojoHandle CoreImpl::AddDispatcherNoLock(scoped_refptr<Dispatcher> dispatcher) {
|
| +MojoHandle CoreImpl::AddDispatcherNoLock(
|
| + const scoped_refptr<Dispatcher>& dispatcher) {
|
| DCHECK(dispatcher.get());
|
| handle_table_lock_.AssertAcquired();
|
| DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID);
|
| @@ -214,7 +382,7 @@ MojoHandle CoreImpl::AddDispatcherNoLock(scoped_refptr<Dispatcher> dispatcher) {
|
| }
|
|
|
| MojoHandle new_handle = next_handle_;
|
| - handle_table_[new_handle] = dispatcher;
|
| + handle_table_[new_handle] = HandleTableEntry(dispatcher);
|
|
|
| next_handle_++;
|
| if (next_handle_ == MOJO_HANDLE_INVALID)
|
| @@ -236,10 +404,10 @@ MojoResult CoreImpl::WaitManyInternal(const MojoHandle* handles,
|
| std::vector<scoped_refptr<Dispatcher> > dispatchers;
|
| dispatchers.reserve(num_handles);
|
| for (uint32_t i = 0; i < num_handles; i++) {
|
| - scoped_refptr<Dispatcher> d = GetDispatcher(handles[i]);
|
| - if (!d.get())
|
| + scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handles[i]);
|
| + if (!dispatcher.get())
|
| return MOJO_RESULT_INVALID_ARGUMENT;
|
| - dispatchers.push_back(d);
|
| + dispatchers.push_back(dispatcher);
|
| }
|
|
|
| // TODO(vtl): Should make the waiter live (permanently) in TLS.
|
|
|