blob: 4ea67bbdc803424b1731fcf384d3acc4ad1670aa [file] [log] [blame]
morrita54f6f80c2014-09-23 21:16:001// 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
amistryd4aa70d2016-06-23 07:52:375#include "ipc/ipc_mojo_bootstrap.h"
morrita54f6f80c2014-09-23 21:16:006
tfarina10a5c062015-09-04 18:47:577#include <stdint.h>
rockot02b8e182016-07-13 20:08:308
9#include <map>
10#include <memory>
rockot9abe09b2016-08-02 20:57:3411#include <queue>
dchenge48600452015-12-28 02:24:5012#include <utility>
rockot0e4de5f2016-07-22 21:18:0713#include <vector>
tfarina10a5c062015-09-04 18:47:5714
rockota21316a2016-06-19 17:08:3615#include "base/callback.h"
morrita54f6f80c2014-09-23 21:16:0016#include "base/logging.h"
avi246998d82015-12-22 02:39:0417#include "base/macros.h"
danakj03de39b22016-04-23 04:21:0918#include "base/memory/ptr_util.h"
rockot02b8e182016-07-13 20:08:3019#include "base/single_thread_task_runner.h"
rockot02b8e182016-07-13 20:08:3020#include "base/synchronization/lock.h"
Sam McNallyde5ae672017-06-19 23:34:4521#include "base/threading/thread_checker.h"
rockot02b8e182016-07-13 20:08:3022#include "base/threading/thread_task_runner_handle.h"
rockot02b8e182016-07-13 20:08:3023#include "mojo/public/cpp/bindings/associated_group.h"
24#include "mojo/public/cpp/bindings/associated_group_controller.h"
rockot02b8e182016-07-13 20:08:3025#include "mojo/public/cpp/bindings/connector.h"
26#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
27#include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
28#include "mojo/public/cpp/bindings/interface_id.h"
rockot0e4de5f2016-07-22 21:18:0729#include "mojo/public/cpp/bindings/message.h"
rockot02b8e182016-07-13 20:08:3030#include "mojo/public/cpp/bindings/message_header_validator.h"
31#include "mojo/public/cpp/bindings/pipe_control_message_handler.h"
32#include "mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h"
33#include "mojo/public/cpp/bindings/pipe_control_message_proxy.h"
rockotb62e2e32017-03-24 18:36:4434#include "mojo/public/cpp/bindings/sync_event_watcher.h"
morrita54f6f80c2014-09-23 21:16:0035
36namespace IPC {
37
38namespace {
39
rockot02b8e182016-07-13 20:08:3040class ChannelAssociatedGroupController
41 : public mojo::AssociatedGroupController,
42 public mojo::MessageReceiver,
43 public mojo::PipeControlMessageHandlerDelegate {
44 public:
rockot0e4de5f2016-07-22 21:18:0745 ChannelAssociatedGroupController(
46 bool set_interface_id_namespace_bit,
47 const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner)
rockotb01ef6a2016-07-27 03:24:3248 : task_runner_(ipc_task_runner),
rockot0e4de5f2016-07-22 21:18:0749 proxy_task_runner_(base::ThreadTaskRunnerHandle::Get()),
50 set_interface_id_namespace_bit_(set_interface_id_namespace_bit),
rockot222e7dd2016-08-24 23:37:1151 filters_(this),
rockot02b8e182016-07-13 20:08:3052 control_message_handler_(this),
rockot0e4de5f2016-07-22 21:18:0753 control_message_proxy_thunk_(this),
54 control_message_proxy_(&control_message_proxy_thunk_) {
55 thread_checker_.DetachFromThread();
rockot02b8e182016-07-13 20:08:3056 control_message_handler_.SetDescription(
57 "IPC::mojom::Bootstrap [master] PipeControlMessageHandler");
rockot222e7dd2016-08-24 23:37:1158 filters_.Append<mojo::MessageHeaderValidator>(
59 "IPC::mojom::Bootstrap [master] MessageHeaderValidator");
rockot02b8e182016-07-13 20:08:3060 }
61
rockot0e4de5f2016-07-22 21:18:0762 void Bind(mojo::ScopedMessagePipeHandle handle) {
63 DCHECK(thread_checker_.CalledOnValidThread());
64 DCHECK(task_runner_->BelongsToCurrentThread());
rockot90984352016-07-25 17:36:1965
rockot0e4de5f2016-07-22 21:18:0766 connector_.reset(new mojo::Connector(
67 std::move(handle), mojo::Connector::SINGLE_THREADED_SEND,
68 task_runner_));
rockot222e7dd2016-08-24 23:37:1169 connector_->set_incoming_receiver(&filters_);
rockot0e4de5f2016-07-22 21:18:0770 connector_->set_connection_error_handler(
71 base::Bind(&ChannelAssociatedGroupController::OnPipeError,
72 base::Unretained(this)));
jcivelli2207af12017-01-26 20:46:0073 connector_->SetWatcherHeapProfilerTag("IPC Channel");
rockot401fb2c2016-09-06 18:35:5774 }
rockot0e4de5f2016-07-22 21:18:0775
rockot10188752016-09-08 18:24:5676 void Pause() {
77 DCHECK(!paused_);
78 paused_ = true;
79 }
80
81 void Unpause() {
82 DCHECK(paused_);
83 paused_ = false;
rockot401fb2c2016-09-06 18:35:5784 }
85
86 void FlushOutgoingMessages() {
rockotc4cc691e2016-08-19 18:48:5787 std::vector<mojo::Message> outgoing_messages;
rockot0e4de5f2016-07-22 21:18:0788 std::swap(outgoing_messages, outgoing_messages_);
89 for (auto& message : outgoing_messages)
rockotc4cc691e2016-08-19 18:48:5790 SendMessage(&message);
rockot0e4de5f2016-07-22 21:18:0791 }
92
93 void CreateChannelEndpoints(mojom::ChannelAssociatedPtr* sender,
94 mojom::ChannelAssociatedRequest* receiver) {
95 mojo::InterfaceId sender_id, receiver_id;
96 if (set_interface_id_namespace_bit_) {
97 sender_id = 1 | mojo::kInterfaceIdNamespaceMask;
98 receiver_id = 1;
99 } else {
100 sender_id = 1;
101 receiver_id = 1 | mojo::kInterfaceIdNamespaceMask;
102 }
103
104 {
105 base::AutoLock locker(lock_);
106 Endpoint* sender_endpoint = new Endpoint(this, sender_id);
107 Endpoint* receiver_endpoint = new Endpoint(this, receiver_id);
108 endpoints_.insert({ sender_id, sender_endpoint });
109 endpoints_.insert({ receiver_id, receiver_endpoint });
yzshen0a5971312017-02-02 05:13:47110 sender_endpoint->set_handle_created();
111 receiver_endpoint->set_handle_created();
rockot0e4de5f2016-07-22 21:18:07112 }
113
114 mojo::ScopedInterfaceEndpointHandle sender_handle =
yzshen2859a2ac2017-02-14 22:24:25115 CreateScopedInterfaceEndpointHandle(sender_id);
rockot0e4de5f2016-07-22 21:18:07116 mojo::ScopedInterfaceEndpointHandle receiver_handle =
yzshen2859a2ac2017-02-14 22:24:25117 CreateScopedInterfaceEndpointHandle(receiver_id);
rockot0e4de5f2016-07-22 21:18:07118
119 sender->Bind(mojom::ChannelAssociatedPtrInfo(std::move(sender_handle), 0));
Ken Rockot96d1b7b52017-05-13 00:29:21120 *receiver = mojom::ChannelAssociatedRequest(std::move(receiver_handle));
rockot0e4de5f2016-07-22 21:18:07121 }
rockot02b8e182016-07-13 20:08:30122
123 void ShutDown() {
124 DCHECK(thread_checker_.CalledOnValidThread());
rockot0e4de5f2016-07-22 21:18:07125 connector_->CloseMessagePipe();
rockot02b8e182016-07-13 20:08:30126 OnPipeError();
rockot0e4de5f2016-07-22 21:18:07127 connector_.reset();
rockot02b8e182016-07-13 20:08:30128 }
129
130 // mojo::AssociatedGroupController:
yzshen2859a2ac2017-02-14 22:24:25131 mojo::InterfaceId AssociateInterface(
132 mojo::ScopedInterfaceEndpointHandle handle_to_send) override {
133 if (!handle_to_send.pending_association())
134 return mojo::kInvalidInterfaceId;
135
rockot02b8e182016-07-13 20:08:30136 uint32_t id = 0;
yzshen2859a2ac2017-02-14 22:24:25137 {
138 base::AutoLock locker(lock_);
139 do {
140 if (next_interface_id_ >= mojo::kInterfaceIdNamespaceMask)
141 next_interface_id_ = 2;
142 id = next_interface_id_++;
143 if (set_interface_id_namespace_bit_)
144 id |= mojo::kInterfaceIdNamespaceMask;
145 } while (ContainsKey(endpoints_, id));
rockot02b8e182016-07-13 20:08:30146
yzshen2859a2ac2017-02-14 22:24:25147 Endpoint* endpoint = new Endpoint(this, id);
148 if (encountered_error_)
149 endpoint->set_peer_closed();
150 endpoint->set_handle_created();
151 endpoints_.insert({id, endpoint});
152 }
rockot02b8e182016-07-13 20:08:30153
yzshen2859a2ac2017-02-14 22:24:25154 if (!NotifyAssociation(&handle_to_send, id)) {
155 // The peer handle of |handle_to_send|, which is supposed to join this
156 // associated group, has been closed.
157 {
158 base::AutoLock locker(lock_);
159 Endpoint* endpoint = FindEndpoint(id);
160 if (endpoint)
161 MarkClosedAndMaybeRemove(endpoint);
162 }
163
164 control_message_proxy_.NotifyPeerEndpointClosed(
165 id, handle_to_send.disconnect_reason());
166 }
167 return id;
rockot02b8e182016-07-13 20:08:30168 }
169
170 mojo::ScopedInterfaceEndpointHandle CreateLocalEndpointHandle(
171 mojo::InterfaceId id) override {
172 if (!mojo::IsValidInterfaceId(id))
173 return mojo::ScopedInterfaceEndpointHandle();
174
Yuzhu Shen9f87fb02017-08-11 17:07:06175 // Unless it is the master ID, |id| is from the remote side and therefore
176 // its namespace bit is supposed to be different than the value that this
177 // router would use.
178 if (!mojo::IsMasterInterfaceId(id) &&
179 set_interface_id_namespace_bit_ ==
180 mojo::HasInterfaceIdNamespaceBitSet(id)) {
181 return mojo::ScopedInterfaceEndpointHandle();
182 }
183
rockot02b8e182016-07-13 20:08:30184 base::AutoLock locker(lock_);
185 bool inserted = false;
186 Endpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
yzshenea784ea2017-01-31 21:20:20187 if (inserted) {
188 DCHECK(!endpoint->handle_created());
189 if (encountered_error_)
190 endpoint->set_peer_closed();
191 } else {
192 if (endpoint->handle_created())
193 return mojo::ScopedInterfaceEndpointHandle();
194 }
rockot02b8e182016-07-13 20:08:30195
yzshenea784ea2017-01-31 21:20:20196 endpoint->set_handle_created();
yzshen2859a2ac2017-02-14 22:24:25197 return CreateScopedInterfaceEndpointHandle(id);
rockot02b8e182016-07-13 20:08:30198 }
199
yzshen8be41d3a2017-01-23 20:40:37200 void CloseEndpointHandle(
201 mojo::InterfaceId id,
yzshen8be41d3a2017-01-23 20:40:37202 const base::Optional<mojo::DisconnectReason>& reason) override {
rockot02b8e182016-07-13 20:08:30203 if (!mojo::IsValidInterfaceId(id))
204 return;
yzshen2859a2ac2017-02-14 22:24:25205 {
206 base::AutoLock locker(lock_);
rockot02b8e182016-07-13 20:08:30207 DCHECK(ContainsKey(endpoints_, id));
yzshen2859a2ac2017-02-14 22:24:25208 Endpoint* endpoint = endpoints_[id].get();
209 DCHECK(!endpoint->client());
210 DCHECK(!endpoint->closed());
211 MarkClosedAndMaybeRemove(endpoint);
rockot02b8e182016-07-13 20:08:30212 }
213
yzshen8be41d3a2017-01-23 20:40:37214 if (!mojo::IsMasterInterfaceId(id) || reason)
215 control_message_proxy_.NotifyPeerEndpointClosed(id, reason);
rockot02b8e182016-07-13 20:08:30216 }
217
218 mojo::InterfaceEndpointController* AttachEndpointClient(
219 const mojo::ScopedInterfaceEndpointHandle& handle,
220 mojo::InterfaceEndpointClient* client,
Sam McNallyde5ae672017-06-19 23:34:45221 scoped_refptr<base::SequencedTaskRunner> runner) override {
rockot02b8e182016-07-13 20:08:30222 const mojo::InterfaceId id = handle.id();
223
224 DCHECK(mojo::IsValidInterfaceId(id));
225 DCHECK(client);
226
227 base::AutoLock locker(lock_);
228 DCHECK(ContainsKey(endpoints_, id));
229
230 Endpoint* endpoint = endpoints_[id].get();
231 endpoint->AttachClient(client, std::move(runner));
232
233 if (endpoint->peer_closed())
234 NotifyEndpointOfError(endpoint, true /* force_async */);
235
236 return endpoint;
237 }
238
239 void DetachEndpointClient(
240 const mojo::ScopedInterfaceEndpointHandle& handle) override {
241 const mojo::InterfaceId id = handle.id();
242
243 DCHECK(mojo::IsValidInterfaceId(id));
244
245 base::AutoLock locker(lock_);
246 DCHECK(ContainsKey(endpoints_, id));
247
248 Endpoint* endpoint = endpoints_[id].get();
249 endpoint->DetachClient();
250 }
251
252 void RaiseError() override {
rockot7604e7b72016-07-28 17:37:39253 if (task_runner_->BelongsToCurrentThread()) {
rockot0e4de5f2016-07-22 21:18:07254 connector_->RaiseError();
rockot02b8e182016-07-13 20:08:30255 } else {
256 task_runner_->PostTask(
257 FROM_HERE,
258 base::Bind(&ChannelAssociatedGroupController::RaiseError, this));
259 }
260 }
261
Ken Rockot474df0142017-07-12 13:28:56262 bool PrefersSerializedMessages() override { return true; }
263
rockot02b8e182016-07-13 20:08:30264 private:
265 class Endpoint;
rockot0e4de5f2016-07-22 21:18:07266 class ControlMessageProxyThunk;
rockot02b8e182016-07-13 20:08:30267 friend class Endpoint;
rockot0e4de5f2016-07-22 21:18:07268 friend class ControlMessageProxyThunk;
rockot02b8e182016-07-13 20:08:30269
yzshen0a5971312017-02-02 05:13:47270 // MessageWrapper objects are always destroyed under the controller's lock. On
271 // destruction, if the message it wrappers contains
272 // ScopedInterfaceEndpointHandles (which cannot be destructed under the
273 // controller's lock), the wrapper unlocks to clean them up.
274 class MessageWrapper {
yzshenea784ea2017-01-31 21:20:20275 public:
yzshen0a5971312017-02-02 05:13:47276 MessageWrapper() = default;
yzshenea784ea2017-01-31 21:20:20277
yzshen0a5971312017-02-02 05:13:47278 MessageWrapper(ChannelAssociatedGroupController* controller,
279 mojo::Message message)
280 : controller_(controller), value_(std::move(message)) {}
yzshenea784ea2017-01-31 21:20:20281
yzshen0a5971312017-02-02 05:13:47282 MessageWrapper(MessageWrapper&& other)
yzshenea784ea2017-01-31 21:20:20283 : controller_(other.controller_), value_(std::move(other.value_)) {}
284
yzshen0a5971312017-02-02 05:13:47285 ~MessageWrapper() {
286 if (value_.associated_endpoint_handles()->empty())
yzshenea784ea2017-01-31 21:20:20287 return;
288
289 controller_->lock_.AssertAcquired();
yzshen0a5971312017-02-02 05:13:47290 {
yzshenea784ea2017-01-31 21:20:20291 base::AutoUnlock unlocker(controller_->lock_);
yzshen0a5971312017-02-02 05:13:47292 value_.mutable_associated_endpoint_handles()->clear();
yzshenea784ea2017-01-31 21:20:20293 }
294 }
295
yzshen0a5971312017-02-02 05:13:47296 MessageWrapper& operator=(MessageWrapper&& other) {
yzshenea784ea2017-01-31 21:20:20297 controller_ = other.controller_;
298 value_ = std::move(other.value_);
299 return *this;
300 }
301
yzshen0a5971312017-02-02 05:13:47302 mojo::Message& value() { return value_; }
yzshenea784ea2017-01-31 21:20:20303
304 private:
305 ChannelAssociatedGroupController* controller_ = nullptr;
yzshenea784ea2017-01-31 21:20:20306 mojo::Message value_;
307
yzshen0a5971312017-02-02 05:13:47308 DISALLOW_COPY_AND_ASSIGN(MessageWrapper);
yzshenea784ea2017-01-31 21:20:20309 };
310
rockot02b8e182016-07-13 20:08:30311 class Endpoint : public base::RefCountedThreadSafe<Endpoint>,
312 public mojo::InterfaceEndpointController {
313 public:
314 Endpoint(ChannelAssociatedGroupController* controller, mojo::InterfaceId id)
315 : controller_(controller), id_(id) {}
316
317 mojo::InterfaceId id() const { return id_; }
318
319 bool closed() const {
320 controller_->lock_.AssertAcquired();
321 return closed_;
322 }
323
324 void set_closed() {
325 controller_->lock_.AssertAcquired();
326 closed_ = true;
327 }
328
329 bool peer_closed() const {
330 controller_->lock_.AssertAcquired();
331 return peer_closed_;
332 }
333
334 void set_peer_closed() {
335 controller_->lock_.AssertAcquired();
336 peer_closed_ = true;
337 }
338
yzshenea784ea2017-01-31 21:20:20339 bool handle_created() const {
340 controller_->lock_.AssertAcquired();
341 return handle_created_;
342 }
343
344 void set_handle_created() {
345 controller_->lock_.AssertAcquired();
346 handle_created_ = true;
347 }
348
yzshen8be41d3a2017-01-23 20:40:37349 const base::Optional<mojo::DisconnectReason>& disconnect_reason() const {
350 return disconnect_reason_;
351 }
352
353 void set_disconnect_reason(
354 const base::Optional<mojo::DisconnectReason>& disconnect_reason) {
355 disconnect_reason_ = disconnect_reason;
356 }
357
Sam McNallyde5ae672017-06-19 23:34:45358 base::SequencedTaskRunner* task_runner() const {
rockot02b8e182016-07-13 20:08:30359 return task_runner_.get();
360 }
361
362 mojo::InterfaceEndpointClient* client() const {
363 controller_->lock_.AssertAcquired();
364 return client_;
365 }
366
367 void AttachClient(mojo::InterfaceEndpointClient* client,
Sam McNallyde5ae672017-06-19 23:34:45368 scoped_refptr<base::SequencedTaskRunner> runner) {
rockot02b8e182016-07-13 20:08:30369 controller_->lock_.AssertAcquired();
370 DCHECK(!client_);
371 DCHECK(!closed_);
peary28cd3bd22017-06-29 02:15:28372 DCHECK(runner->RunsTasksInCurrentSequence());
rockot02b8e182016-07-13 20:08:30373
374 task_runner_ = std::move(runner);
375 client_ = client;
376 }
377
378 void DetachClient() {
379 controller_->lock_.AssertAcquired();
380 DCHECK(client_);
peary28cd3bd22017-06-29 02:15:28381 DCHECK(task_runner_->RunsTasksInCurrentSequence());
rockot02b8e182016-07-13 20:08:30382 DCHECK(!closed_);
383
384 task_runner_ = nullptr;
385 client_ = nullptr;
rockot9abe09b2016-08-02 20:57:34386 sync_watcher_.reset();
387 }
388
yzshen0a5971312017-02-02 05:13:47389 uint32_t EnqueueSyncMessage(MessageWrapper message) {
rockot9abe09b2016-08-02 20:57:34390 controller_->lock_.AssertAcquired();
391 uint32_t id = GenerateSyncMessageId();
392 sync_messages_.emplace(id, std::move(message));
393 SignalSyncMessageEvent();
394 return id;
395 }
396
397 void SignalSyncMessageEvent() {
398 controller_->lock_.AssertAcquired();
yzshene25b5d52017-02-28 21:56:31399
400 if (sync_message_event_)
401 sync_message_event_->Signal();
rockot9abe09b2016-08-02 20:57:34402 }
403
yzshen0a5971312017-02-02 05:13:47404 MessageWrapper PopSyncMessage(uint32_t id) {
rockot9abe09b2016-08-02 20:57:34405 controller_->lock_.AssertAcquired();
406 if (sync_messages_.empty() || sync_messages_.front().first != id)
yzshen0a5971312017-02-02 05:13:47407 return MessageWrapper();
408 MessageWrapper message = std::move(sync_messages_.front().second);
rockot9abe09b2016-08-02 20:57:34409 sync_messages_.pop();
410 return message;
rockot02b8e182016-07-13 20:08:30411 }
412
413 // mojo::InterfaceEndpointController:
414 bool SendMessage(mojo::Message* message) override {
peary28cd3bd22017-06-29 02:15:28415 DCHECK(task_runner_->RunsTasksInCurrentSequence());
rockot02b8e182016-07-13 20:08:30416 message->set_interface_id(id_);
417 return controller_->SendMessage(message);
418 }
419
420 void AllowWokenUpBySyncWatchOnSameThread() override {
peary28cd3bd22017-06-29 02:15:28421 DCHECK(task_runner_->RunsTasksInCurrentSequence());
rockot02b8e182016-07-13 20:08:30422
rockot9abe09b2016-08-02 20:57:34423 EnsureSyncWatcherExists();
424 sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
rockot02b8e182016-07-13 20:08:30425 }
426
427 bool SyncWatch(const bool* should_stop) override {
peary28cd3bd22017-06-29 02:15:28428 DCHECK(task_runner_->RunsTasksInCurrentSequence());
rockot02b8e182016-07-13 20:08:30429
430 // It's not legal to make sync calls from the master endpoint's thread,
431 // and in fact they must only happen from the proxy task runner.
rockot7604e7b72016-07-28 17:37:39432 DCHECK(!controller_->task_runner_->BelongsToCurrentThread());
rockot02b8e182016-07-13 20:08:30433 DCHECK(controller_->proxy_task_runner_->BelongsToCurrentThread());
434
rockot9abe09b2016-08-02 20:57:34435 EnsureSyncWatcherExists();
436 return sync_watcher_->SyncWatch(should_stop);
rockot02b8e182016-07-13 20:08:30437 }
438
439 private:
440 friend class base::RefCountedThreadSafe<Endpoint>;
441
rockot9abe09b2016-08-02 20:57:34442 ~Endpoint() override {
443 controller_->lock_.AssertAcquired();
444 DCHECK(!client_);
445 DCHECK(closed_);
446 DCHECK(peer_closed_);
447 DCHECK(!sync_watcher_);
448 }
449
rockotb62e2e32017-03-24 18:36:44450 void OnSyncMessageEventReady() {
peary28cd3bd22017-06-29 02:15:28451 DCHECK(task_runner_->RunsTasksInCurrentSequence());
rockot9abe09b2016-08-02 20:57:34452
453 scoped_refptr<Endpoint> keepalive(this);
454 scoped_refptr<AssociatedGroupController> controller_keepalive(
455 controller_);
456
457 bool reset_sync_watcher = false;
458 {
459 base::AutoLock locker(controller_->lock_);
460 bool more_to_process = false;
461 if (!sync_messages_.empty()) {
yzshen0a5971312017-02-02 05:13:47462 MessageWrapper message_wrapper =
yzshenea784ea2017-01-31 21:20:20463 std::move(sync_messages_.front().second);
rockot9abe09b2016-08-02 20:57:34464 sync_messages_.pop();
465
466 bool dispatch_succeeded;
467 mojo::InterfaceEndpointClient* client = client_;
468 {
469 base::AutoUnlock unlocker(controller_->lock_);
yzshen0a5971312017-02-02 05:13:47470 dispatch_succeeded =
471 client->HandleIncomingMessage(&message_wrapper.value());
rockot9abe09b2016-08-02 20:57:34472 }
473
474 if (!sync_messages_.empty())
475 more_to_process = true;
476
477 if (!dispatch_succeeded)
478 controller_->RaiseError();
479 }
480
481 if (!more_to_process)
482 sync_message_event_->Reset();
483
484 // If there are no queued sync messages and the peer has closed, there
485 // there won't be incoming sync messages in the future.
486 reset_sync_watcher = !more_to_process && peer_closed_;
487 }
488
489 if (reset_sync_watcher) {
490 // If a SyncWatch() call (or multiple ones) of this interface endpoint
491 // is on the call stack, resetting the sync watcher will allow it to
492 // exit when the call stack unwinds to that frame.
493 sync_watcher_.reset();
494 }
495 }
496
497 void EnsureSyncWatcherExists() {
peary28cd3bd22017-06-29 02:15:28498 DCHECK(task_runner_->RunsTasksInCurrentSequence());
rockot9abe09b2016-08-02 20:57:34499 if (sync_watcher_)
500 return;
501
502 {
503 base::AutoLock locker(controller_->lock_);
yzshene25b5d52017-02-28 21:56:31504 if (!sync_message_event_) {
rockotb62e2e32017-03-24 18:36:44505 sync_message_event_ = base::MakeUnique<base::WaitableEvent>(
506 base::WaitableEvent::ResetPolicy::MANUAL,
507 base::WaitableEvent::InitialState::NOT_SIGNALED);
yzshene25b5d52017-02-28 21:56:31508 if (peer_closed_ || !sync_messages_.empty())
509 SignalSyncMessageEvent();
510 }
rockot9abe09b2016-08-02 20:57:34511 }
512
rockotb62e2e32017-03-24 18:36:44513 sync_watcher_ = base::MakeUnique<mojo::SyncEventWatcher>(
514 sync_message_event_.get(),
515 base::Bind(&Endpoint::OnSyncMessageEventReady,
516 base::Unretained(this)));
rockot9abe09b2016-08-02 20:57:34517 }
518
519 uint32_t GenerateSyncMessageId() {
520 // Overflow is fine.
521 uint32_t id = next_sync_message_id_++;
522 DCHECK(sync_messages_.empty() || sync_messages_.front().first != id);
523 return id;
524 }
rockot02b8e182016-07-13 20:08:30525
526 ChannelAssociatedGroupController* const controller_;
527 const mojo::InterfaceId id_;
528
529 bool closed_ = false;
530 bool peer_closed_ = false;
yzshenea784ea2017-01-31 21:20:20531 bool handle_created_ = false;
yzshen8be41d3a2017-01-23 20:40:37532 base::Optional<mojo::DisconnectReason> disconnect_reason_;
rockot02b8e182016-07-13 20:08:30533 mojo::InterfaceEndpointClient* client_ = nullptr;
Sam McNallyde5ae672017-06-19 23:34:45534 scoped_refptr<base::SequencedTaskRunner> task_runner_;
rockotb62e2e32017-03-24 18:36:44535 std::unique_ptr<mojo::SyncEventWatcher> sync_watcher_;
536 std::unique_ptr<base::WaitableEvent> sync_message_event_;
yzshen0a5971312017-02-02 05:13:47537 std::queue<std::pair<uint32_t, MessageWrapper>> sync_messages_;
rockot9abe09b2016-08-02 20:57:34538 uint32_t next_sync_message_id_ = 0;
rockot02b8e182016-07-13 20:08:30539
540 DISALLOW_COPY_AND_ASSIGN(Endpoint);
541 };
542
rockot0e4de5f2016-07-22 21:18:07543 class ControlMessageProxyThunk : public MessageReceiver {
544 public:
545 explicit ControlMessageProxyThunk(
546 ChannelAssociatedGroupController* controller)
547 : controller_(controller) {}
548
549 private:
550 // MessageReceiver:
551 bool Accept(mojo::Message* message) override {
552 return controller_->SendMessage(message);
553 }
554
555 ChannelAssociatedGroupController* controller_;
556
557 DISALLOW_COPY_AND_ASSIGN(ControlMessageProxyThunk);
558 };
559
rockot02b8e182016-07-13 20:08:30560 ~ChannelAssociatedGroupController() override {
rockotb01ef6a2016-07-27 03:24:32561 DCHECK(!connector_);
562
rockot02b8e182016-07-13 20:08:30563 base::AutoLock locker(lock_);
rockot02b8e182016-07-13 20:08:30564 for (auto iter = endpoints_.begin(); iter != endpoints_.end();) {
565 Endpoint* endpoint = iter->second.get();
566 ++iter;
567
yzshene003d592017-01-24 21:42:17568 if (!endpoint->closed()) {
569 // This happens when a NotifyPeerEndpointClosed message been received,
yzshen2859a2ac2017-02-14 22:24:25570 // but the interface ID hasn't been used to create local endpoint
571 // handle.
yzshene003d592017-01-24 21:42:17572 DCHECK(!endpoint->client());
573 DCHECK(endpoint->peer_closed());
574 MarkClosedAndMaybeRemove(endpoint);
575 } else {
576 MarkPeerClosedAndMaybeRemove(endpoint);
577 }
rockot02b8e182016-07-13 20:08:30578 }
579
580 DCHECK(endpoints_.empty());
581 }
582
583 bool SendMessage(mojo::Message* message) {
rockot7604e7b72016-07-28 17:37:39584 if (task_runner_->BelongsToCurrentThread()) {
rockot02b8e182016-07-13 20:08:30585 DCHECK(thread_checker_.CalledOnValidThread());
rockot10188752016-09-08 18:24:56586 if (!connector_ || paused_) {
rockotc4cc691e2016-08-19 18:48:57587 outgoing_messages_.emplace_back(std::move(*message));
rockot0e4de5f2016-07-22 21:18:07588 return true;
589 }
590 return connector_->Accept(message);
rockot02b8e182016-07-13 20:08:30591 } else {
rockotbecd3f742016-11-08 20:47:00592 // We always post tasks to the master endpoint thread when called from
593 // other threads in order to simulate IPC::ChannelProxy::Send behavior.
rockot02b8e182016-07-13 20:08:30594 task_runner_->PostTask(
595 FROM_HERE,
596 base::Bind(
597 &ChannelAssociatedGroupController::SendMessageOnMasterThread,
rockotc4cc691e2016-08-19 18:48:57598 this, base::Passed(message)));
rockot02b8e182016-07-13 20:08:30599 return true;
600 }
601 }
602
rockotc4cc691e2016-08-19 18:48:57603 void SendMessageOnMasterThread(mojo::Message message) {
rockot02b8e182016-07-13 20:08:30604 DCHECK(thread_checker_.CalledOnValidThread());
rockotc4cc691e2016-08-19 18:48:57605 if (!SendMessage(&message))
rockot02b8e182016-07-13 20:08:30606 RaiseError();
607 }
608
609 void OnPipeError() {
610 DCHECK(thread_checker_.CalledOnValidThread());
611
612 // We keep |this| alive here because it's possible for the notifications
613 // below to release all other references.
614 scoped_refptr<ChannelAssociatedGroupController> keepalive(this);
615
616 base::AutoLock locker(lock_);
617 encountered_error_ = true;
618
619 std::vector<scoped_refptr<Endpoint>> endpoints_to_notify;
620 for (auto iter = endpoints_.begin(); iter != endpoints_.end();) {
621 Endpoint* endpoint = iter->second.get();
622 ++iter;
623
624 if (endpoint->client())
625 endpoints_to_notify.push_back(endpoint);
626
627 MarkPeerClosedAndMaybeRemove(endpoint);
628 }
629
630 for (auto& endpoint : endpoints_to_notify) {
rockot0e4de5f2016-07-22 21:18:07631 // Because a notification may in turn detach any endpoint, we have to
rockot02b8e182016-07-13 20:08:30632 // check each client again here.
633 if (endpoint->client())
634 NotifyEndpointOfError(endpoint.get(), false /* force_async */);
635 }
636 }
637
638 void NotifyEndpointOfError(Endpoint* endpoint, bool force_async) {
639 lock_.AssertAcquired();
640 DCHECK(endpoint->task_runner() && endpoint->client());
peary28cd3bd22017-06-29 02:15:28641 if (endpoint->task_runner()->RunsTasksInCurrentSequence() && !force_async) {
rockot02b8e182016-07-13 20:08:30642 mojo::InterfaceEndpointClient* client = endpoint->client();
yzshen8be41d3a2017-01-23 20:40:37643 base::Optional<mojo::DisconnectReason> reason(
644 endpoint->disconnect_reason());
rockot02b8e182016-07-13 20:08:30645
646 base::AutoUnlock unlocker(lock_);
yzshen8be41d3a2017-01-23 20:40:37647 client->NotifyError(reason);
rockot02b8e182016-07-13 20:08:30648 } else {
649 endpoint->task_runner()->PostTask(
650 FROM_HERE,
tzik1d692a2e2017-07-03 11:01:26651 base::Bind(&ChannelAssociatedGroupController::
652 NotifyEndpointOfErrorOnEndpointThread,
653 this, endpoint->id(), base::Unretained(endpoint)));
rockot02b8e182016-07-13 20:08:30654 }
655 }
656
rockot9abe09b2016-08-02 20:57:34657 void NotifyEndpointOfErrorOnEndpointThread(mojo::InterfaceId id,
658 Endpoint* endpoint) {
rockot02b8e182016-07-13 20:08:30659 base::AutoLock locker(lock_);
rockot9abe09b2016-08-02 20:57:34660 auto iter = endpoints_.find(id);
661 if (iter == endpoints_.end() || iter->second.get() != endpoint)
662 return;
rockot02b8e182016-07-13 20:08:30663 if (!endpoint->client())
664 return;
rockot9abe09b2016-08-02 20:57:34665
peary28cd3bd22017-06-29 02:15:28666 DCHECK(endpoint->task_runner()->RunsTasksInCurrentSequence());
rockot9abe09b2016-08-02 20:57:34667 NotifyEndpointOfError(endpoint, false /* force_async */);
rockot02b8e182016-07-13 20:08:30668 }
669
670 void MarkClosedAndMaybeRemove(Endpoint* endpoint) {
671 lock_.AssertAcquired();
672 endpoint->set_closed();
673 if (endpoint->closed() && endpoint->peer_closed())
674 endpoints_.erase(endpoint->id());
675 }
676
677 void MarkPeerClosedAndMaybeRemove(Endpoint* endpoint) {
678 lock_.AssertAcquired();
679 endpoint->set_peer_closed();
rockot9abe09b2016-08-02 20:57:34680 endpoint->SignalSyncMessageEvent();
rockot02b8e182016-07-13 20:08:30681 if (endpoint->closed() && endpoint->peer_closed())
682 endpoints_.erase(endpoint->id());
683 }
684
685 Endpoint* FindOrInsertEndpoint(mojo::InterfaceId id, bool* inserted) {
686 lock_.AssertAcquired();
687 DCHECK(!inserted || !*inserted);
688
yzshen0a5971312017-02-02 05:13:47689 Endpoint* endpoint = FindEndpoint(id);
690 if (!endpoint) {
691 endpoint = new Endpoint(this, id);
692 endpoints_.insert({id, endpoint});
693 if (inserted)
694 *inserted = true;
695 }
rockot02b8e182016-07-13 20:08:30696 return endpoint;
697 }
698
yzshen0a5971312017-02-02 05:13:47699 Endpoint* FindEndpoint(mojo::InterfaceId id) {
700 lock_.AssertAcquired();
701 auto iter = endpoints_.find(id);
702 return iter != endpoints_.end() ? iter->second.get() : nullptr;
703 }
704
rockot02b8e182016-07-13 20:08:30705 // mojo::MessageReceiver:
706 bool Accept(mojo::Message* message) override {
707 DCHECK(thread_checker_.CalledOnValidThread());
708
yzshen0a5971312017-02-02 05:13:47709 if (!message->DeserializeAssociatedEndpointHandles(this))
710 return false;
711
712 if (mojo::PipeControlMessageHandler::IsPipeControlMessage(message))
713 return control_message_handler_.Accept(message);
rockot02b8e182016-07-13 20:08:30714
715 mojo::InterfaceId id = message->interface_id();
716 DCHECK(mojo::IsValidInterfaceId(id));
717
718 base::AutoLock locker(lock_);
yzshen0a5971312017-02-02 05:13:47719 Endpoint* endpoint = FindEndpoint(id);
720 if (!endpoint)
721 return true;
722
723 mojo::InterfaceEndpointClient* client = endpoint->client();
peary28cd3bd22017-06-29 02:15:28724 if (!client || !endpoint->task_runner()->RunsTasksInCurrentSequence()) {
rockot02b8e182016-07-13 20:08:30725 // No client has been bound yet or the client runs tasks on another
726 // thread. We assume the other thread must always be the one on which
727 // |proxy_task_runner_| runs tasks, since that's the only valid scenario.
728 //
729 // If the client is not yet bound, it must be bound by the time this task
730 // runs or else it's programmer error.
731 DCHECK(proxy_task_runner_);
rockot9abe09b2016-08-02 20:57:34732
rockotc4cc691e2016-08-19 18:48:57733 if (message->has_flag(mojo::Message::kFlagIsSync)) {
yzshen0a5971312017-02-02 05:13:47734 MessageWrapper message_wrapper(this, std::move(*message));
rockot9abe09b2016-08-02 20:57:34735 // Sync messages may need to be handled by the endpoint if it's blocking
736 // on a sync reply. We pass ownership of the message to the endpoint's
737 // sync message queue. If the endpoint was blocking, it will dequeue the
738 // message and dispatch it. Otherwise the posted |AcceptSyncMessage()|
739 // call will dequeue the message and dispatch it.
yzshenea784ea2017-01-31 21:20:20740 uint32_t message_id =
741 endpoint->EnqueueSyncMessage(std::move(message_wrapper));
rockot9abe09b2016-08-02 20:57:34742 proxy_task_runner_->PostTask(
743 FROM_HERE,
744 base::Bind(&ChannelAssociatedGroupController::AcceptSyncMessage,
745 this, id, message_id));
746 return true;
747 }
748
rockot02b8e182016-07-13 20:08:30749 proxy_task_runner_->PostTask(
750 FROM_HERE,
751 base::Bind(&ChannelAssociatedGroupController::AcceptOnProxyThread,
rockotc4cc691e2016-08-19 18:48:57752 this, base::Passed(message)));
rockot02b8e182016-07-13 20:08:30753 return true;
754 }
755
756 // We do not expect to receive sync responses on the master endpoint thread.
757 // If it's happening, it's a bug.
rockot9abe09b2016-08-02 20:57:34758 DCHECK(!message->has_flag(mojo::Message::kFlagIsSync) ||
759 !message->has_flag(mojo::Message::kFlagIsResponse));
rockot02b8e182016-07-13 20:08:30760
rockot8d890f62016-07-14 16:37:14761 base::AutoUnlock unlocker(lock_);
yzshen0a5971312017-02-02 05:13:47762 return client->HandleIncomingMessage(message);
rockot02b8e182016-07-13 20:08:30763 }
764
rockotc4cc691e2016-08-19 18:48:57765 void AcceptOnProxyThread(mojo::Message message) {
rockot02b8e182016-07-13 20:08:30766 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
767
rockotc4cc691e2016-08-19 18:48:57768 mojo::InterfaceId id = message.interface_id();
rockot8d890f62016-07-14 16:37:14769 DCHECK(mojo::IsValidInterfaceId(id) && !mojo::IsMasterInterfaceId(id));
770
771 base::AutoLock locker(lock_);
yzshen0a5971312017-02-02 05:13:47772 Endpoint* endpoint = FindEndpoint(id);
rockot8d890f62016-07-14 16:37:14773 if (!endpoint)
774 return;
775
776 mojo::InterfaceEndpointClient* client = endpoint->client();
777 if (!client)
778 return;
779
peary28cd3bd22017-06-29 02:15:28780 DCHECK(endpoint->task_runner()->RunsTasksInCurrentSequence());
rockot8d890f62016-07-14 16:37:14781
rockot9abe09b2016-08-02 20:57:34782 // Sync messages should never make their way to this method.
yzshen0a5971312017-02-02 05:13:47783 DCHECK(!message.has_flag(mojo::Message::kFlagIsSync));
rockot8d890f62016-07-14 16:37:14784
785 bool result = false;
786 {
787 base::AutoUnlock unlocker(lock_);
yzshen0a5971312017-02-02 05:13:47788 result = client->HandleIncomingMessage(&message);
rockot8d890f62016-07-14 16:37:14789 }
790
791 if (!result)
792 RaiseError();
793 }
794
rockot9abe09b2016-08-02 20:57:34795 void AcceptSyncMessage(mojo::InterfaceId interface_id, uint32_t message_id) {
796 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
797
798 base::AutoLock locker(lock_);
yzshen0a5971312017-02-02 05:13:47799 Endpoint* endpoint = FindEndpoint(interface_id);
rockot9abe09b2016-08-02 20:57:34800 if (!endpoint)
801 return;
802
csharrison1af8d6ab2017-04-21 17:47:23803 // Careful, if the endpoint is detached its members are cleared. Check for
804 // that before dereferencing.
805 mojo::InterfaceEndpointClient* client = endpoint->client();
806 if (!client)
807 return;
808
peary28cd3bd22017-06-29 02:15:28809 DCHECK(endpoint->task_runner()->RunsTasksInCurrentSequence());
yzshen0a5971312017-02-02 05:13:47810 MessageWrapper message_wrapper = endpoint->PopSyncMessage(message_id);
rockot9abe09b2016-08-02 20:57:34811
812 // The message must have already been dequeued by the endpoint waking up
813 // from a sync wait. Nothing to do.
yzshenea784ea2017-01-31 21:20:20814 if (message_wrapper.value().IsNull())
rockot9abe09b2016-08-02 20:57:34815 return;
816
rockot9abe09b2016-08-02 20:57:34817 bool result = false;
818 {
819 base::AutoUnlock unlocker(lock_);
yzshen0a5971312017-02-02 05:13:47820 result = client->HandleIncomingMessage(&message_wrapper.value());
rockot9abe09b2016-08-02 20:57:34821 }
822
823 if (!result)
824 RaiseError();
825 }
826
rockot02b8e182016-07-13 20:08:30827 // mojo::PipeControlMessageHandlerDelegate:
yzshen8be41d3a2017-01-23 20:40:37828 bool OnPeerAssociatedEndpointClosed(
829 mojo::InterfaceId id,
830 const base::Optional<mojo::DisconnectReason>& reason) override {
rockot02b8e182016-07-13 20:08:30831 DCHECK(thread_checker_.CalledOnValidThread());
832
yzshen8be41d3a2017-01-23 20:40:37833 DCHECK(!mojo::IsMasterInterfaceId(id) || reason);
rockot02b8e182016-07-13 20:08:30834
rockot0e4de5f2016-07-22 21:18:07835 scoped_refptr<ChannelAssociatedGroupController> keepalive(this);
rockot02b8e182016-07-13 20:08:30836 base::AutoLock locker(lock_);
837 scoped_refptr<Endpoint> endpoint = FindOrInsertEndpoint(id, nullptr);
yzshen8be41d3a2017-01-23 20:40:37838 if (reason)
839 endpoint->set_disconnect_reason(reason);
rockot02b8e182016-07-13 20:08:30840 if (!endpoint->peer_closed()) {
841 if (endpoint->client())
842 NotifyEndpointOfError(endpoint.get(), false /* force_async */);
843 MarkPeerClosedAndMaybeRemove(endpoint.get());
844 }
845
846 return true;
847 }
848
rockot02b8e182016-07-13 20:08:30849 // Checked in places which must be run on the master endpoint's thread.
850 base::ThreadChecker thread_checker_;
851
852 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
rockot0e4de5f2016-07-22 21:18:07853
rockot02b8e182016-07-13 20:08:30854 scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
rockot0e4de5f2016-07-22 21:18:07855 const bool set_interface_id_namespace_bit_;
rockot10188752016-09-08 18:24:56856 bool paused_ = false;
rockot0e4de5f2016-07-22 21:18:07857 std::unique_ptr<mojo::Connector> connector_;
rockot222e7dd2016-08-24 23:37:11858 mojo::FilterChain filters_;
rockot02b8e182016-07-13 20:08:30859 mojo::PipeControlMessageHandler control_message_handler_;
rockot0e4de5f2016-07-22 21:18:07860 ControlMessageProxyThunk control_message_proxy_thunk_;
rockot58909542016-11-10 20:05:45861
862 // NOTE: It is unsafe to call into this object while holding |lock_|.
rockot0e4de5f2016-07-22 21:18:07863 mojo::PipeControlMessageProxy control_message_proxy_;
864
865 // Outgoing messages that were sent before this controller was bound to a
866 // real message pipe.
rockotc4cc691e2016-08-19 18:48:57867 std::vector<mojo::Message> outgoing_messages_;
rockot02b8e182016-07-13 20:08:30868
869 // Guards the fields below for thread-safe access.
870 base::Lock lock_;
871
872 bool encountered_error_ = false;
rockot0e4de5f2016-07-22 21:18:07873
874 // ID #1 is reserved for the mojom::Channel interface.
875 uint32_t next_interface_id_ = 2;
876
rockot02b8e182016-07-13 20:08:30877 std::map<uint32_t, scoped_refptr<Endpoint>> endpoints_;
rockot02b8e182016-07-13 20:08:30878
879 DISALLOW_COPY_AND_ASSIGN(ChannelAssociatedGroupController);
880};
881
rockot0e4de5f2016-07-22 21:18:07882class MojoBootstrapImpl : public MojoBootstrap {
rockot02b8e182016-07-13 20:08:30883 public:
rockot0e4de5f2016-07-22 21:18:07884 MojoBootstrapImpl(
885 mojo::ScopedMessagePipeHandle handle,
rockot0e4de5f2016-07-22 21:18:07886 const scoped_refptr<ChannelAssociatedGroupController> controller)
yzshen2859a2ac2017-02-14 22:24:25887 : controller_(controller),
888 associated_group_(controller),
889 handle_(std::move(handle)) {}
rockot02b8e182016-07-13 20:08:30890
rockot0e4de5f2016-07-22 21:18:07891 ~MojoBootstrapImpl() override {
892 controller_->ShutDown();
rockot02b8e182016-07-13 20:08:30893 }
894
895 private:
rockota628d0b2017-02-09 08:40:15896 void Connect(mojom::ChannelAssociatedPtr* sender,
897 mojom::ChannelAssociatedRequest* receiver) override {
rockot0e4de5f2016-07-22 21:18:07898 controller_->Bind(std::move(handle_));
rockota628d0b2017-02-09 08:40:15899 controller_->CreateChannelEndpoints(sender, receiver);
msramek5507fee2016-07-22 10:06:21900 }
901
rockot10188752016-09-08 18:24:56902 void Pause() override {
903 controller_->Pause();
904 }
905
906 void Unpause() override {
907 controller_->Unpause();
rockot401fb2c2016-09-06 18:35:57908 }
909
910 void Flush() override {
911 controller_->FlushOutgoingMessages();
912 }
913
msramek5507fee2016-07-22 10:06:21914 mojo::AssociatedGroup* GetAssociatedGroup() override {
yzshen2859a2ac2017-02-14 22:24:25915 return &associated_group_;
msramek5507fee2016-07-22 10:06:21916 }
917
rockot0e4de5f2016-07-22 21:18:07918 scoped_refptr<ChannelAssociatedGroupController> controller_;
yzshen2859a2ac2017-02-14 22:24:25919 mojo::AssociatedGroup associated_group_;
msramek5507fee2016-07-22 10:06:21920
rockot0e4de5f2016-07-22 21:18:07921 mojo::ScopedMessagePipeHandle handle_;
msramek5507fee2016-07-22 10:06:21922
rockot0e4de5f2016-07-22 21:18:07923 DISALLOW_COPY_AND_ASSIGN(MojoBootstrapImpl);
msramek5507fee2016-07-22 10:06:21924};
925
morrita54f6f80c2014-09-23 21:16:00926} // namespace
927
morrita54f6f80c2014-09-23 21:16:00928// static
danakj03de39b22016-04-23 04:21:09929std::unique_ptr<MojoBootstrap> MojoBootstrap::Create(
sammc57ed9f982016-03-10 06:28:35930 mojo::ScopedMessagePipeHandle handle,
931 Channel::Mode mode,
rockot0e4de5f2016-07-22 21:18:07932 const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner) {
933 return base::MakeUnique<MojoBootstrapImpl>(
rockota628d0b2017-02-09 08:40:15934 std::move(handle), new ChannelAssociatedGroupController(
935 mode == Channel::MODE_SERVER, ipc_task_runner));
sammc57ed9f982016-03-10 06:28:35936}
937
morrita54f6f80c2014-09-23 21:16:00938} // namespace IPC