| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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/edk/system/broker_state.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/rand_util.h" | |
| 12 #include "mojo/edk/embedder/embedder_internal.h" | |
| 13 #include "mojo/edk/embedder/platform_channel_pair.h" | |
| 14 #include "mojo/edk/system/child_broker_host.h" | |
| 15 #include "mojo/edk/system/message_pipe_dispatcher.h" | |
| 16 #include "mojo/edk/system/routed_raw_channel.h" | |
| 17 | |
| 18 namespace mojo { | |
| 19 namespace edk { | |
| 20 | |
| 21 BrokerState* BrokerState::GetInstance() { | |
| 22 return base::Singleton< | |
| 23 BrokerState, base::LeakySingletonTraits<BrokerState>>::get(); | |
| 24 } | |
| 25 | |
| 26 #if defined(OS_WIN) | |
| 27 void BrokerState::CreatePlatformChannelPair( | |
| 28 ScopedPlatformHandle* server, ScopedPlatformHandle* client) { | |
| 29 PlatformChannelPair channel_pair; | |
| 30 *server = channel_pair.PassServerHandle(); | |
| 31 *client = channel_pair.PassClientHandle(); | |
| 32 } | |
| 33 | |
| 34 void BrokerState::HandleToToken( | |
| 35 const PlatformHandle* platform_handles, | |
| 36 size_t count, | |
| 37 uint64_t* tokens) { | |
| 38 base::AutoLock auto_locker(token_map_lock_); | |
| 39 for (size_t i = 0; i < count; ++i) { | |
| 40 if (platform_handles[i].is_valid()) { | |
| 41 uint64_t token; | |
| 42 do { | |
| 43 token = base::RandUint64(); | |
| 44 } while (!token || token_map_.find(token) != token_map_.end()); | |
| 45 tokens[i] = token; | |
| 46 token_map_[tokens[i]] = platform_handles[i].handle; | |
| 47 } else { | |
| 48 DLOG(WARNING) << "BrokerState got invalid handle."; | |
| 49 tokens[i] = 0; | |
| 50 } | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 void BrokerState::TokenToHandle(const uint64_t* tokens, | |
| 55 size_t count, | |
| 56 PlatformHandle* handles) { | |
| 57 base::AutoLock auto_locker(token_map_lock_); | |
| 58 for (size_t i = 0; i < count; ++i) { | |
| 59 auto it = token_map_.find(tokens[i]); | |
| 60 if (it == token_map_.end()) { | |
| 61 DLOG(WARNING) << "TokenToHandle didn't find token."; | |
| 62 } else { | |
| 63 handles[i].handle = it->second; | |
| 64 token_map_.erase(it); | |
| 65 } | |
| 66 } | |
| 67 } | |
| 68 #endif | |
| 69 | |
| 70 void BrokerState::ConnectMessagePipe(uint64_t pipe_id, | |
| 71 MessagePipeDispatcher* message_pipe) { | |
| 72 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 73 base::AutoLock auto_lock(lock_); | |
| 74 if (pending_connects_.find(pipe_id) != pending_connects_.end()) { | |
| 75 // Both ends of the message pipe are in this process. | |
| 76 if (!in_process_pipes_channel1_) { | |
| 77 PlatformChannelPair channel_pair; | |
| 78 in_process_pipes_channel1_ = new RoutedRawChannel( | |
| 79 channel_pair.PassServerHandle(), | |
| 80 base::Bind(&BrokerState::ChannelDestructed, base::Unretained(this))); | |
| 81 in_process_pipes_channel2_ = new RoutedRawChannel( | |
| 82 channel_pair.PassClientHandle(), | |
| 83 base::Bind(&BrokerState::ChannelDestructed, base::Unretained(this))); | |
| 84 } | |
| 85 | |
| 86 AttachMessagePipe(pending_connects_[pipe_id], pipe_id, | |
| 87 in_process_pipes_channel1_); | |
| 88 AttachMessagePipe(message_pipe, pipe_id, in_process_pipes_channel2_); | |
| 89 pending_connects_.erase(pipe_id); | |
| 90 return; | |
| 91 } | |
| 92 | |
| 93 if (pending_child_connects_.find(pipe_id) != pending_child_connects_.end()) { | |
| 94 // A child process has already tried to connect. | |
| 95 ChildBrokerHost* child_host = pending_child_connects_[pipe_id]; | |
| 96 if (child_host && child_host->channel()) { | |
| 97 AttachMessagePipe(message_pipe, pipe_id, child_host->channel()); | |
| 98 child_host->ConnectMessagePipe(pipe_id, 0); | |
| 99 } else { | |
| 100 message_pipe->OnError(RawChannel::Delegate::ERROR_READ_SHUTDOWN); | |
| 101 } | |
| 102 | |
| 103 pending_child_connects_.erase(pipe_id); | |
| 104 return; | |
| 105 } | |
| 106 | |
| 107 pending_connects_[pipe_id] = message_pipe; | |
| 108 } | |
| 109 | |
| 110 void BrokerState::CloseMessagePipe(uint64_t pipe_id, | |
| 111 MessagePipeDispatcher* message_pipe) { | |
| 112 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 113 | |
| 114 CHECK(connected_pipes_.find(message_pipe) != connected_pipes_.end()); | |
| 115 connected_pipes_[message_pipe]->RemoveRoute(pipe_id); | |
| 116 connected_pipes_.erase(message_pipe); | |
| 117 } | |
| 118 | |
| 119 void BrokerState::ChildBrokerHostCreated(ChildBrokerHost* child_broker_host) { | |
| 120 base::AutoLock auto_lock(lock_); | |
| 121 CHECK(child_processes_.find(child_broker_host->GetProcessId()) == | |
| 122 child_processes_.end()); | |
| 123 child_processes_[child_broker_host->GetProcessId()] = child_broker_host; | |
| 124 } | |
| 125 | |
| 126 void BrokerState::ChildBrokerHostDestructed( | |
| 127 ChildBrokerHost* child_broker_host) { | |
| 128 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 129 base::AutoLock auto_lock(lock_); | |
| 130 | |
| 131 for (auto it = pending_child_connects_.begin(); | |
| 132 it != pending_child_connects_.end(); ++it) { | |
| 133 if (it->second == child_broker_host) { | |
| 134 // Signify that the process has died. When another process tries to | |
| 135 // connect to the message pipe, we will tell it that the peer has died so | |
| 136 // that it can fire a peer closed notification. | |
| 137 it->second = nullptr; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 base::ProcessId pid = child_broker_host->GetProcessId(); | |
| 142 for (auto it = connected_processes_.begin(); | |
| 143 it != connected_processes_.end();) { | |
| 144 if ((*it).first == pid || (*it).second == pid) { | |
| 145 // Since we can't do it = connected_processes_.erase(it); until hash_map | |
| 146 // uses unordered_map on posix. | |
| 147 auto cur = it++; | |
| 148 connected_processes_.erase(cur); | |
| 149 } else { | |
| 150 it++; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 CHECK(child_processes_.find(pid) != child_processes_.end()); | |
| 155 child_processes_.erase(pid); | |
| 156 } | |
| 157 | |
| 158 void BrokerState::HandleConnectMessagePipe(ChildBrokerHost* pipe_process, | |
| 159 uint64_t pipe_id) { | |
| 160 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 161 base::AutoLock auto_lock(lock_); | |
| 162 if (pending_child_connects_.find(pipe_id) != pending_child_connects_.end()) { | |
| 163 // Another child process is waiting to connect to the given pipe. | |
| 164 ChildBrokerHost* pending_pipe_process = pending_child_connects_[pipe_id]; | |
| 165 if (pending_pipe_process && pending_pipe_process->channel()) { | |
| 166 EnsureProcessesConnected(pipe_process->GetProcessId(), | |
| 167 pending_pipe_process->GetProcessId()); | |
| 168 pending_pipe_process->ConnectMessagePipe( | |
| 169 pipe_id, pipe_process->GetProcessId()); | |
| 170 pipe_process->ConnectMessagePipe( | |
| 171 pipe_id, pending_pipe_process->GetProcessId()); | |
| 172 } else { | |
| 173 pipe_process->PeerDied(pipe_id); | |
| 174 } | |
| 175 pending_child_connects_.erase(pipe_id); | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 if (pending_connects_.find(pipe_id) != pending_connects_.end()) { | |
| 180 // This parent process is the other side of the given pipe. | |
| 181 MessagePipeDispatcher* pending_pipe = pending_connects_[pipe_id]; | |
| 182 AttachMessagePipe(pending_pipe, pipe_id, pipe_process->channel()); | |
| 183 pipe_process->ConnectMessagePipe(pipe_id, 0); | |
| 184 pending_connects_.erase(pipe_id); | |
| 185 return; | |
| 186 } | |
| 187 | |
| 188 // This is the first connection request for pipe_id to reach the parent. | |
| 189 pending_child_connects_[pipe_id] = pipe_process; | |
| 190 } | |
| 191 | |
| 192 void BrokerState::HandleCancelConnectMessagePipe(uint64_t pipe_id) { | |
| 193 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 194 base::AutoLock auto_lock(lock_); | |
| 195 if (pending_child_connects_.find(pipe_id) == pending_child_connects_.end()) { | |
| 196 NOTREACHED() << "Can't find entry for pipe_id " << pipe_id; | |
| 197 } else { | |
| 198 pending_child_connects_.erase(pipe_id); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 BrokerState::BrokerState() | |
| 203 : in_process_pipes_channel1_(nullptr), | |
| 204 in_process_pipes_channel2_(nullptr) { | |
| 205 DCHECK(!internal::g_broker); | |
| 206 internal::g_broker = this; | |
| 207 } | |
| 208 | |
| 209 BrokerState::~BrokerState() { | |
| 210 } | |
| 211 | |
| 212 void BrokerState::EnsureProcessesConnected(base::ProcessId pid1, | |
| 213 base::ProcessId pid2) { | |
| 214 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 215 lock_.AssertAcquired(); | |
| 216 CHECK_NE(pid1, pid2); | |
| 217 CHECK_NE(pid1, base::GetCurrentProcId()); | |
| 218 CHECK_NE(pid2, base::GetCurrentProcId()); | |
| 219 std::pair<base::ProcessId, base::ProcessId> processes; | |
| 220 processes.first = std::min(pid1, pid2); | |
| 221 processes.second = std::max(pid1, pid2); | |
| 222 if (connected_processes_.find(processes) != connected_processes_.end()) | |
| 223 return; | |
| 224 | |
| 225 connected_processes_.insert(processes); | |
| 226 PlatformChannelPair channel_pair; | |
| 227 CHECK(child_processes_.find(pid1) != child_processes_.end()); | |
| 228 CHECK(child_processes_.find(pid2) != child_processes_.end()); | |
| 229 child_processes_[pid1]->ConnectToProcess(pid2, | |
| 230 channel_pair.PassServerHandle()); | |
| 231 child_processes_[pid2]->ConnectToProcess(pid1, | |
| 232 channel_pair.PassClientHandle()); | |
| 233 } | |
| 234 | |
| 235 void BrokerState::ChannelDestructed(RoutedRawChannel* channel) { | |
| 236 } | |
| 237 | |
| 238 void BrokerState::AttachMessagePipe(MessagePipeDispatcher* message_pipe, | |
| 239 uint64_t pipe_id, | |
| 240 RoutedRawChannel* raw_channel) { | |
| 241 connected_pipes_[message_pipe] = raw_channel; | |
| 242 // Note: we must call GotNonTransferableChannel before AddRoute because there | |
| 243 // could be race conditions if the pipe got queued messages in |AddRoute| but | |
| 244 // then when it's read it returns no messages because it doesn't have the | |
| 245 // channel yet. | |
| 246 message_pipe->GotNonTransferableChannel(raw_channel->channel()); | |
| 247 // The above call could have caused |CloseMessagePipe| to be called. | |
| 248 if (connected_pipes_.find(message_pipe) != connected_pipes_.end()) | |
| 249 raw_channel->AddRoute(pipe_id, message_pipe); | |
| 250 } | |
| 251 | |
| 252 } // namespace edk | |
| 253 } // namespace mojo | |
| OLD | NEW |