summaryrefslogtreecommitdiffstats
path: root/ipc/mojo/scoped_ipc_support.cc
blob: 15575444e64f15dac84c32131ff126004dd8affc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ipc/mojo/scoped_ipc_support.h"

#include <stddef.h>

#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/thread_task_runner_handle.h"
#include "third_party/mojo/src/mojo/edk/embedder/embedder.h"
#include "third_party/mojo/src/mojo/edk/embedder/process_delegate.h"

namespace IPC {

namespace {
// TODO(use_chrome_edk)
//class IPCSupportInitializer : public mojo::edk::ProcessDelegate {
class IPCSupportInitializer : public mojo::embedder::ProcessDelegate {
 public:
  IPCSupportInitializer()
      : init_count_(0),
        shutting_down_(false),
        was_shut_down_(false),
        observer_(nullptr) {}

  ~IPCSupportInitializer() override { DCHECK(!observer_); }

  void Init(scoped_refptr<base::TaskRunner> io_thread_task_runner);
  void ShutDown(bool force);

 private:
  // This watches for destruction of the MessageLoop that IPCSupportInitializer
  // uses for IO, and guarantees that the initializer is shut down if it still
  // exists when the loop is being destroyed.
  class MessageLoopObserver : public base::MessageLoop::DestructionObserver {
   public:
    MessageLoopObserver(IPCSupportInitializer* initializer)
        : initializer_(initializer) {}

    ~MessageLoopObserver() override {
      base::MessageLoop::current()->RemoveDestructionObserver(this);
    }

   private:
    // base::MessageLoop::DestructionObserver:
    void WillDestroyCurrentMessageLoop() override {
      initializer_->ShutDown(true);
    }

    IPCSupportInitializer* initializer_;

    DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
  };

  void ShutDownOnIOThread();

  // mojo::embedder::ProcessDelegate:
  void OnShutdownComplete() override {}

  static void WatchMessageLoopOnIOThread(MessageLoopObserver* observer);

  base::Lock lock_;
  size_t init_count_;
  bool shutting_down_;

  // This is used to track whether shutdown has occurred yet, since we can be
  // shut down by either the scoper or IO MessageLoop destruction.
  bool was_shut_down_;

  // The message loop destruction observer we have watching our IO loop. This
  // is created on the initializer's own thread but is used and destroyed on the
  // IO thread.
  MessageLoopObserver* observer_;

  scoped_refptr<base::TaskRunner> io_thread_task_runner_;

  DISALLOW_COPY_AND_ASSIGN(IPCSupportInitializer);
};

void IPCSupportInitializer::Init(
    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
  base::AutoLock locker(lock_);
  DCHECK((init_count_ == 0 && !io_thread_task_runner_) ||
         io_thread_task_runner_ == io_thread_task_runner);

  if (shutting_down_) {
    // If reinitialized before a pending shutdown task is executed, we
    // effectively cancel the shutdown task.
    DCHECK(init_count_ == 1);
    shutting_down_ = false;
    return;
  }

  init_count_++;
  if (init_count_ == 1) {
    was_shut_down_ = false;
    observer_ = new MessageLoopObserver(this);
    io_thread_task_runner_ = io_thread_task_runner;
    io_thread_task_runner_->PostTask(
        FROM_HERE, base::Bind(&WatchMessageLoopOnIOThread, observer_));
    mojo::embedder::InitIPCSupport(
        mojo::embedder::ProcessType::NONE, this, io_thread_task_runner_,
        mojo::embedder::ScopedPlatformHandle());
  }
}

void IPCSupportInitializer::ShutDown(bool force) {
  base::AutoLock locker(lock_);
  if (shutting_down_ || was_shut_down_)
    return;
  DCHECK(init_count_ > 0);
  if (init_count_ > 1 && !force) {
    init_count_--;
    return;
  }

  shutting_down_ = true;
  if (base::MessageLoop::current() &&
      base::MessageLoop::current()->task_runner() == io_thread_task_runner_) {
    base::AutoUnlock unlocker_(lock_);
    ShutDownOnIOThread();
  } else {
    io_thread_task_runner_->PostTask(
        FROM_HERE, base::Bind(&IPCSupportInitializer::ShutDownOnIOThread,
                              base::Unretained(this)));
  }
}

void IPCSupportInitializer::ShutDownOnIOThread() {
  base::AutoLock locker(lock_);
  if (shutting_down_ && !was_shut_down_) {
    mojo::embedder::ShutdownIPCSupportOnIOThread();
    init_count_ = 0;
    shutting_down_ = false;
    io_thread_task_runner_ = nullptr;
    was_shut_down_ = true;
    if (observer_) {
      delete observer_;
      observer_ = nullptr;
    }
  }
}

// static
void IPCSupportInitializer::WatchMessageLoopOnIOThread(
    MessageLoopObserver* observer) {
  base::MessageLoop::current()->AddDestructionObserver(observer);
}

base::LazyInstance<IPCSupportInitializer>::Leaky ipc_support_initializer;

}  // namespace

ScopedIPCSupport::ScopedIPCSupport(
    scoped_refptr<base::TaskRunner> io_thread_task_runner) {
  ipc_support_initializer.Get().Init(io_thread_task_runner);
}

ScopedIPCSupport::~ScopedIPCSupport() {
  ipc_support_initializer.Get().ShutDown(false);
}

}  // namespace IPC