1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/base/nsMessageLoop.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,162 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsMessageLoop.h" 1.10 +#include "mozilla/WeakPtr.h" 1.11 +#include "base/message_loop.h" 1.12 +#include "base/task.h" 1.13 +#include "nsIRunnable.h" 1.14 +#include "nsITimer.h" 1.15 +#include "nsCOMPtr.h" 1.16 +#include "nsAutoPtr.h" 1.17 +#include "nsComponentManagerUtils.h" 1.18 +#include "nsThreadUtils.h" 1.19 + 1.20 +using namespace mozilla; 1.21 + 1.22 +namespace { 1.23 + 1.24 +/** 1.25 + * This Task runs its nsIRunnable when Run() is called, or after 1.26 + * aEnsureRunsAfterMS milliseconds have elapsed since the object was 1.27 + * constructed. 1.28 + * 1.29 + * Note that the MessageLoop owns this object and will delete it after it calls 1.30 + * Run(). Tread lightly. 1.31 + */ 1.32 +class MessageLoopIdleTask 1.33 + : public Task 1.34 + , public SupportsWeakPtr<MessageLoopIdleTask> 1.35 +{ 1.36 +public: 1.37 + MOZ_DECLARE_REFCOUNTED_TYPENAME(MessageLoopIdleTask) 1.38 + MessageLoopIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS); 1.39 + virtual ~MessageLoopIdleTask() {} 1.40 + virtual void Run(); 1.41 + 1.42 +private: 1.43 + nsresult Init(uint32_t aEnsureRunsAfterMS); 1.44 + 1.45 + nsCOMPtr<nsIRunnable> mTask; 1.46 + nsCOMPtr<nsITimer> mTimer; 1.47 +}; 1.48 + 1.49 +/** 1.50 + * This timer callback calls MessageLoopIdleTask::Run() when its timer fires. 1.51 + * (The timer can't call back into MessageLoopIdleTask directly since that's 1.52 + * not a refcounted object; it's owned by the MessageLoop.) 1.53 + * 1.54 + * We keep a weak reference to the MessageLoopIdleTask, although a raw pointer 1.55 + * should in theory suffice: When the MessageLoopIdleTask runs (right before 1.56 + * the MessageLoop deletes it), it cancels its timer. But the weak pointer 1.57 + * saves us from worrying about an edge case somehow messing us up here. 1.58 + */ 1.59 +class MessageLoopTimerCallback 1.60 + : public nsITimerCallback 1.61 +{ 1.62 +public: 1.63 + MessageLoopTimerCallback(MessageLoopIdleTask* aTask); 1.64 + virtual ~MessageLoopTimerCallback() {}; 1.65 + 1.66 + NS_DECL_ISUPPORTS 1.67 + NS_DECL_NSITIMERCALLBACK 1.68 + 1.69 +private: 1.70 + WeakPtr<MessageLoopIdleTask> mTask; 1.71 +}; 1.72 + 1.73 +MessageLoopIdleTask::MessageLoopIdleTask(nsIRunnable* aTask, 1.74 + uint32_t aEnsureRunsAfterMS) 1.75 + : mTask(aTask) 1.76 +{ 1.77 + // Init() really shouldn't fail, but if it does, we schedule our runnable 1.78 + // immediately, because it's more important to guarantee that we run the task 1.79 + // eventually than it is to run the task when we're idle. 1.80 + nsresult rv = Init(aEnsureRunsAfterMS); 1.81 + if (NS_FAILED(rv)) { 1.82 + NS_WARNING("Running idle task early because we couldn't initialize our timer."); 1.83 + NS_DispatchToCurrentThread(mTask); 1.84 + 1.85 + mTask = nullptr; 1.86 + mTimer = nullptr; 1.87 + } 1.88 +} 1.89 + 1.90 +nsresult 1.91 +MessageLoopIdleTask::Init(uint32_t aEnsureRunsAfterMS) 1.92 +{ 1.93 + mTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.94 + if (NS_WARN_IF(!mTimer)) 1.95 + return NS_ERROR_UNEXPECTED; 1.96 + 1.97 + nsRefPtr<MessageLoopTimerCallback> callback = 1.98 + new MessageLoopTimerCallback(this); 1.99 + 1.100 + return mTimer->InitWithCallback(callback, aEnsureRunsAfterMS, 1.101 + nsITimer::TYPE_ONE_SHOT); 1.102 +} 1.103 + 1.104 +/* virtual */ void 1.105 +MessageLoopIdleTask::Run() 1.106 +{ 1.107 + // Null out our pointers because if Run() was called by the timer, this 1.108 + // object will be kept alive by the MessageLoop until the MessageLoop calls 1.109 + // Run(). 1.110 + 1.111 + if (mTimer) { 1.112 + mTimer->Cancel(); 1.113 + mTimer = nullptr; 1.114 + } 1.115 + 1.116 + if (mTask) { 1.117 + mTask->Run(); 1.118 + mTask = nullptr; 1.119 + } 1.120 +} 1.121 + 1.122 +MessageLoopTimerCallback::MessageLoopTimerCallback(MessageLoopIdleTask* aTask) 1.123 + : mTask(aTask->asWeakPtr()) 1.124 +{} 1.125 + 1.126 +NS_IMETHODIMP 1.127 +MessageLoopTimerCallback::Notify(nsITimer* aTimer) 1.128 +{ 1.129 + // We don't expect to hit the case when the timer fires but mTask has been 1.130 + // deleted, because mTask should cancel the timer before the mTask is 1.131 + // deleted. But you never know... 1.132 + NS_WARN_IF_FALSE(mTask, "This timer shouldn't have fired."); 1.133 + 1.134 + if (mTask) { 1.135 + mTask->Run(); 1.136 + } 1.137 + return NS_OK; 1.138 +} 1.139 + 1.140 +NS_IMPL_ISUPPORTS(MessageLoopTimerCallback, nsITimerCallback) 1.141 + 1.142 +} // anonymous namespace 1.143 + 1.144 +NS_IMPL_ISUPPORTS(nsMessageLoop, nsIMessageLoop) 1.145 + 1.146 +NS_IMETHODIMP 1.147 +nsMessageLoop::PostIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS) 1.148 +{ 1.149 + // The message loop owns MessageLoopIdleTask and deletes it after calling 1.150 + // Run(). Be careful... 1.151 + MessageLoop::current()->PostIdleTask(FROM_HERE, 1.152 + new MessageLoopIdleTask(aTask, aEnsureRunsAfterMS)); 1.153 + return NS_OK; 1.154 +} 1.155 + 1.156 +nsresult 1.157 +nsMessageLoopConstructor(nsISupports* aOuter, 1.158 + const nsIID& aIID, 1.159 + void** aInstancePtr) 1.160 +{ 1.161 + if (NS_WARN_IF(aOuter)) 1.162 + return NS_ERROR_NO_AGGREGATION; 1.163 + nsISupports* messageLoop = new nsMessageLoop(); 1.164 + return messageLoop->QueryInterface(aIID, aInstancePtr); 1.165 +}