ipc/glue/MessagePump.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "MessagePump.h"
michael@0 6
michael@0 7 #include "nsIRunnable.h"
michael@0 8 #include "nsIThread.h"
michael@0 9 #include "nsITimer.h"
michael@0 10
michael@0 11 #include "base/basictypes.h"
michael@0 12 #include "base/logging.h"
michael@0 13 #include "base/scoped_nsautorelease_pool.h"
michael@0 14 #include "mozilla/Assertions.h"
michael@0 15 #include "mozilla/DebugOnly.h"
michael@0 16 #include "nsComponentManagerUtils.h"
michael@0 17 #include "nsDebug.h"
michael@0 18 #include "nsServiceManagerUtils.h"
michael@0 19 #include "nsString.h"
michael@0 20 #include "nsThreadUtils.h"
michael@0 21 #include "nsTimerImpl.h"
michael@0 22 #include "nsXULAppAPI.h"
michael@0 23 #include "prthread.h"
michael@0 24
michael@0 25 #ifdef MOZ_WIDGET_ANDROID
michael@0 26 #include "AndroidBridge.h"
michael@0 27 #endif
michael@0 28
michael@0 29 #ifdef MOZ_NUWA_PROCESS
michael@0 30 #include "ipc/Nuwa.h"
michael@0 31 #endif
michael@0 32
michael@0 33 using base::TimeTicks;
michael@0 34 using namespace mozilla::ipc;
michael@0 35
michael@0 36 NS_DEFINE_NAMED_CID(NS_TIMER_CID);
michael@0 37
michael@0 38 static mozilla::DebugOnly<MessagePump::Delegate*> gFirstDelegate;
michael@0 39
michael@0 40 namespace mozilla {
michael@0 41 namespace ipc {
michael@0 42
michael@0 43 class DoWorkRunnable MOZ_FINAL : public nsIRunnable,
michael@0 44 public nsITimerCallback
michael@0 45 {
michael@0 46 public:
michael@0 47 DoWorkRunnable(MessagePump* aPump)
michael@0 48 : mPump(aPump)
michael@0 49 {
michael@0 50 MOZ_ASSERT(aPump);
michael@0 51 }
michael@0 52
michael@0 53 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 54 NS_DECL_NSIRUNNABLE
michael@0 55 NS_DECL_NSITIMERCALLBACK
michael@0 56
michael@0 57 private:
michael@0 58 ~DoWorkRunnable()
michael@0 59 { }
michael@0 60
michael@0 61 MessagePump* mPump;
michael@0 62 };
michael@0 63
michael@0 64 } /* namespace ipc */
michael@0 65 } /* namespace mozilla */
michael@0 66
michael@0 67 MessagePump::MessagePump()
michael@0 68 : mThread(nullptr)
michael@0 69 {
michael@0 70 mDoWorkEvent = new DoWorkRunnable(this);
michael@0 71 }
michael@0 72
michael@0 73 MessagePump::~MessagePump()
michael@0 74 {
michael@0 75 }
michael@0 76
michael@0 77 void
michael@0 78 MessagePump::Run(MessagePump::Delegate* aDelegate)
michael@0 79 {
michael@0 80 MOZ_ASSERT(keep_running_);
michael@0 81 MOZ_ASSERT(NS_IsMainThread(),
michael@0 82 "Use mozilla::ipc::MessagePumpForNonMainThreads instead!");
michael@0 83
michael@0 84 mThread = NS_GetCurrentThread();
michael@0 85 MOZ_ASSERT(mThread);
michael@0 86
michael@0 87 mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
michael@0 88 MOZ_ASSERT(mDelayedWorkTimer);
michael@0 89
michael@0 90 base::ScopedNSAutoreleasePool autoReleasePool;
michael@0 91
michael@0 92 for (;;) {
michael@0 93 autoReleasePool.Recycle();
michael@0 94
michael@0 95 bool did_work = NS_ProcessNextEvent(mThread, false) ? true : false;
michael@0 96 if (!keep_running_)
michael@0 97 break;
michael@0 98
michael@0 99 // NB: it is crucial *not* to directly call |aDelegate->DoWork()|
michael@0 100 // here. To ensure that MessageLoop tasks and XPCOM events have
michael@0 101 // equal priority, we sensitively rely on processing exactly one
michael@0 102 // Task per DoWorkRunnable XPCOM event.
michael@0 103
michael@0 104 #ifdef MOZ_WIDGET_ANDROID
michael@0 105 // This processes messages in the Android Looper. Note that we only
michael@0 106 // get here if the normal Gecko event loop has been awoken above.
michael@0 107 // Bug 750713
michael@0 108 if (MOZ_LIKELY(AndroidBridge::HasEnv())) {
michael@0 109 did_work |= mozilla::widget::android::GeckoAppShell::PumpMessageLoop();
michael@0 110 }
michael@0 111 #endif
michael@0 112
michael@0 113 did_work |= aDelegate->DoDelayedWork(&delayed_work_time_);
michael@0 114
michael@0 115 if (did_work && delayed_work_time_.is_null()
michael@0 116 #ifdef MOZ_NUWA_PROCESS
michael@0 117 && (!IsNuwaReady() || !IsNuwaProcess())
michael@0 118 #endif
michael@0 119 )
michael@0 120 mDelayedWorkTimer->Cancel();
michael@0 121
michael@0 122 if (!keep_running_)
michael@0 123 break;
michael@0 124
michael@0 125 if (did_work)
michael@0 126 continue;
michael@0 127
michael@0 128 did_work = aDelegate->DoIdleWork();
michael@0 129 if (!keep_running_)
michael@0 130 break;
michael@0 131
michael@0 132 if (did_work)
michael@0 133 continue;
michael@0 134
michael@0 135 // This will either sleep or process an event.
michael@0 136 NS_ProcessNextEvent(mThread, true);
michael@0 137 }
michael@0 138
michael@0 139 #ifdef MOZ_NUWA_PROCESS
michael@0 140 if (!IsNuwaReady() || !IsNuwaProcess())
michael@0 141 #endif
michael@0 142 mDelayedWorkTimer->Cancel();
michael@0 143
michael@0 144 keep_running_ = true;
michael@0 145 }
michael@0 146
michael@0 147 void
michael@0 148 MessagePump::ScheduleWork()
michael@0 149 {
michael@0 150 // Make sure the event loop wakes up.
michael@0 151 if (mThread) {
michael@0 152 mThread->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL);
michael@0 153 }
michael@0 154 else {
michael@0 155 // Some things (like xpcshell) don't use the app shell and so Run hasn't
michael@0 156 // been called. We still need to wake up the main thread.
michael@0 157 NS_DispatchToMainThread(mDoWorkEvent, NS_DISPATCH_NORMAL);
michael@0 158 }
michael@0 159 event_.Signal();
michael@0 160 }
michael@0 161
michael@0 162 void
michael@0 163 MessagePump::ScheduleWorkForNestedLoop()
michael@0 164 {
michael@0 165 // This method is called when our MessageLoop has just allowed
michael@0 166 // nested tasks. In our setup, whenever that happens we know that
michael@0 167 // DoWork() will be called "soon", so there's no need to pay the
michael@0 168 // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent).
michael@0 169 }
michael@0 170
michael@0 171 void
michael@0 172 MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime)
michael@0 173 {
michael@0 174 #ifdef MOZ_NUWA_PROCESS
michael@0 175 if (IsNuwaReady() && IsNuwaProcess())
michael@0 176 return;
michael@0 177 #endif
michael@0 178
michael@0 179 if (!mDelayedWorkTimer) {
michael@0 180 mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
michael@0 181 if (!mDelayedWorkTimer) {
michael@0 182 // Called before XPCOM has started up? We can't do this correctly.
michael@0 183 NS_WARNING("Delayed task might not run!");
michael@0 184 delayed_work_time_ = aDelayedTime;
michael@0 185 return;
michael@0 186 }
michael@0 187 }
michael@0 188
michael@0 189 if (!delayed_work_time_.is_null()) {
michael@0 190 mDelayedWorkTimer->Cancel();
michael@0 191 }
michael@0 192
michael@0 193 delayed_work_time_ = aDelayedTime;
michael@0 194
michael@0 195 // TimeDelta's constructor initializes to 0
michael@0 196 base::TimeDelta delay;
michael@0 197 if (aDelayedTime > base::TimeTicks::Now())
michael@0 198 delay = aDelayedTime - base::TimeTicks::Now();
michael@0 199
michael@0 200 uint32_t delayMS = uint32_t(delay.InMilliseconds());
michael@0 201 mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS,
michael@0 202 nsITimer::TYPE_ONE_SHOT);
michael@0 203 }
michael@0 204
michael@0 205 void
michael@0 206 MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate)
michael@0 207 {
michael@0 208 aDelegate->DoDelayedWork(&delayed_work_time_);
michael@0 209 if (!delayed_work_time_.is_null()) {
michael@0 210 ScheduleDelayedWork(delayed_work_time_);
michael@0 211 }
michael@0 212 }
michael@0 213
michael@0 214 NS_IMPL_ISUPPORTS(DoWorkRunnable, nsIRunnable, nsITimerCallback)
michael@0 215
michael@0 216 NS_IMETHODIMP
michael@0 217 DoWorkRunnable::Run()
michael@0 218 {
michael@0 219 MessageLoop* loop = MessageLoop::current();
michael@0 220 MOZ_ASSERT(loop);
michael@0 221
michael@0 222 bool nestableTasksAllowed = loop->NestableTasksAllowed();
michael@0 223
michael@0 224 // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will
michael@0 225 // always dispatch DoWork() below from what looks to MessageLoop like a nested
michael@0 226 // context. So we unconditionally allow nesting here.
michael@0 227 loop->SetNestableTasksAllowed(true);
michael@0 228 loop->DoWork();
michael@0 229 loop->SetNestableTasksAllowed(nestableTasksAllowed);
michael@0 230
michael@0 231 return NS_OK;
michael@0 232 }
michael@0 233
michael@0 234 NS_IMETHODIMP
michael@0 235 DoWorkRunnable::Notify(nsITimer* aTimer)
michael@0 236 {
michael@0 237 MessageLoop* loop = MessageLoop::current();
michael@0 238 MOZ_ASSERT(loop);
michael@0 239
michael@0 240 mPump->DoDelayedWork(loop);
michael@0 241
michael@0 242 return NS_OK;
michael@0 243 }
michael@0 244
michael@0 245 void
michael@0 246 MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate)
michael@0 247 {
michael@0 248 if (mFirstRun) {
michael@0 249 MOZ_ASSERT(aDelegate && !gFirstDelegate);
michael@0 250 gFirstDelegate = aDelegate;
michael@0 251
michael@0 252 mFirstRun = false;
michael@0 253 if (NS_FAILED(XRE_RunAppShell())) {
michael@0 254 NS_WARNING("Failed to run app shell?!");
michael@0 255 }
michael@0 256
michael@0 257 MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
michael@0 258 gFirstDelegate = nullptr;
michael@0 259
michael@0 260 return;
michael@0 261 }
michael@0 262
michael@0 263 MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
michael@0 264
michael@0 265 // We can get to this point in startup with Tasks in our loop's
michael@0 266 // incoming_queue_ or pending_queue_, but without a matching
michael@0 267 // DoWorkRunnable(). In MessagePump::Run() above, we sensitively
michael@0 268 // depend on *not* directly calling delegate->DoWork(), because that
michael@0 269 // prioritizes Tasks above XPCOM events. However, from this point
michael@0 270 // forward, any Task posted to our loop is guaranteed to have a
michael@0 271 // DoWorkRunnable enqueued for it.
michael@0 272 //
michael@0 273 // So we just flush the pending work here and move on.
michael@0 274 MessageLoop* loop = MessageLoop::current();
michael@0 275 bool nestableTasksAllowed = loop->NestableTasksAllowed();
michael@0 276 loop->SetNestableTasksAllowed(true);
michael@0 277
michael@0 278 while (aDelegate->DoWork());
michael@0 279
michael@0 280 loop->SetNestableTasksAllowed(nestableTasksAllowed);
michael@0 281
michael@0 282 // Really run.
michael@0 283 mozilla::ipc::MessagePump::Run(aDelegate);
michael@0 284 }
michael@0 285
michael@0 286 void
michael@0 287 MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate)
michael@0 288 {
michael@0 289 MOZ_ASSERT(keep_running_);
michael@0 290 MOZ_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
michael@0 291
michael@0 292 mThread = NS_GetCurrentThread();
michael@0 293 MOZ_ASSERT(mThread);
michael@0 294
michael@0 295 mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
michael@0 296 MOZ_ASSERT(mDelayedWorkTimer);
michael@0 297
michael@0 298 if (NS_FAILED(mDelayedWorkTimer->SetTarget(mThread))) {
michael@0 299 MOZ_CRASH("Failed to set timer target!");
michael@0 300 }
michael@0 301
michael@0 302 base::ScopedNSAutoreleasePool autoReleasePool;
michael@0 303
michael@0 304 for (;;) {
michael@0 305 autoReleasePool.Recycle();
michael@0 306
michael@0 307 bool didWork = NS_ProcessNextEvent(mThread, false) ? true : false;
michael@0 308 if (!keep_running_) {
michael@0 309 break;
michael@0 310 }
michael@0 311
michael@0 312 didWork |= aDelegate->DoDelayedWork(&delayed_work_time_);
michael@0 313
michael@0 314 if (didWork && delayed_work_time_.is_null()) {
michael@0 315 mDelayedWorkTimer->Cancel();
michael@0 316 }
michael@0 317
michael@0 318 if (!keep_running_) {
michael@0 319 break;
michael@0 320 }
michael@0 321
michael@0 322 if (didWork) {
michael@0 323 continue;
michael@0 324 }
michael@0 325
michael@0 326 didWork = aDelegate->DoIdleWork();
michael@0 327 if (!keep_running_) {
michael@0 328 break;
michael@0 329 }
michael@0 330
michael@0 331 if (didWork) {
michael@0 332 continue;
michael@0 333 }
michael@0 334
michael@0 335 // This will either sleep or process an event.
michael@0 336 NS_ProcessNextEvent(mThread, true);
michael@0 337 }
michael@0 338
michael@0 339 mDelayedWorkTimer->Cancel();
michael@0 340
michael@0 341 keep_running_ = true;
michael@0 342 }

mercurial