michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsMessageLoop.h" michael@0: #include "mozilla/WeakPtr.h" michael@0: #include "base/message_loop.h" michael@0: #include "base/task.h" michael@0: #include "nsIRunnable.h" michael@0: #include "nsITimer.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: namespace { michael@0: michael@0: /** michael@0: * This Task runs its nsIRunnable when Run() is called, or after michael@0: * aEnsureRunsAfterMS milliseconds have elapsed since the object was michael@0: * constructed. michael@0: * michael@0: * Note that the MessageLoop owns this object and will delete it after it calls michael@0: * Run(). Tread lightly. michael@0: */ michael@0: class MessageLoopIdleTask michael@0: : public Task michael@0: , public SupportsWeakPtr michael@0: { michael@0: public: michael@0: MOZ_DECLARE_REFCOUNTED_TYPENAME(MessageLoopIdleTask) michael@0: MessageLoopIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS); michael@0: virtual ~MessageLoopIdleTask() {} michael@0: virtual void Run(); michael@0: michael@0: private: michael@0: nsresult Init(uint32_t aEnsureRunsAfterMS); michael@0: michael@0: nsCOMPtr mTask; michael@0: nsCOMPtr mTimer; michael@0: }; michael@0: michael@0: /** michael@0: * This timer callback calls MessageLoopIdleTask::Run() when its timer fires. michael@0: * (The timer can't call back into MessageLoopIdleTask directly since that's michael@0: * not a refcounted object; it's owned by the MessageLoop.) michael@0: * michael@0: * We keep a weak reference to the MessageLoopIdleTask, although a raw pointer michael@0: * should in theory suffice: When the MessageLoopIdleTask runs (right before michael@0: * the MessageLoop deletes it), it cancels its timer. But the weak pointer michael@0: * saves us from worrying about an edge case somehow messing us up here. michael@0: */ michael@0: class MessageLoopTimerCallback michael@0: : public nsITimerCallback michael@0: { michael@0: public: michael@0: MessageLoopTimerCallback(MessageLoopIdleTask* aTask); michael@0: virtual ~MessageLoopTimerCallback() {}; michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSITIMERCALLBACK michael@0: michael@0: private: michael@0: WeakPtr mTask; michael@0: }; michael@0: michael@0: MessageLoopIdleTask::MessageLoopIdleTask(nsIRunnable* aTask, michael@0: uint32_t aEnsureRunsAfterMS) michael@0: : mTask(aTask) michael@0: { michael@0: // Init() really shouldn't fail, but if it does, we schedule our runnable michael@0: // immediately, because it's more important to guarantee that we run the task michael@0: // eventually than it is to run the task when we're idle. michael@0: nsresult rv = Init(aEnsureRunsAfterMS); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Running idle task early because we couldn't initialize our timer."); michael@0: NS_DispatchToCurrentThread(mTask); michael@0: michael@0: mTask = nullptr; michael@0: mTimer = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: MessageLoopIdleTask::Init(uint32_t aEnsureRunsAfterMS) michael@0: { michael@0: mTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: if (NS_WARN_IF(!mTimer)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsRefPtr callback = michael@0: new MessageLoopTimerCallback(this); michael@0: michael@0: return mTimer->InitWithCallback(callback, aEnsureRunsAfterMS, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: /* virtual */ void michael@0: MessageLoopIdleTask::Run() michael@0: { michael@0: // Null out our pointers because if Run() was called by the timer, this michael@0: // object will be kept alive by the MessageLoop until the MessageLoop calls michael@0: // Run(). michael@0: michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: mTimer = nullptr; michael@0: } michael@0: michael@0: if (mTask) { michael@0: mTask->Run(); michael@0: mTask = nullptr; michael@0: } michael@0: } michael@0: michael@0: MessageLoopTimerCallback::MessageLoopTimerCallback(MessageLoopIdleTask* aTask) michael@0: : mTask(aTask->asWeakPtr()) michael@0: {} michael@0: michael@0: NS_IMETHODIMP michael@0: MessageLoopTimerCallback::Notify(nsITimer* aTimer) michael@0: { michael@0: // We don't expect to hit the case when the timer fires but mTask has been michael@0: // deleted, because mTask should cancel the timer before the mTask is michael@0: // deleted. But you never know... michael@0: NS_WARN_IF_FALSE(mTask, "This timer shouldn't have fired."); michael@0: michael@0: if (mTask) { michael@0: mTask->Run(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(MessageLoopTimerCallback, nsITimerCallback) michael@0: michael@0: } // anonymous namespace michael@0: michael@0: NS_IMPL_ISUPPORTS(nsMessageLoop, nsIMessageLoop) michael@0: michael@0: NS_IMETHODIMP michael@0: nsMessageLoop::PostIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS) michael@0: { michael@0: // The message loop owns MessageLoopIdleTask and deletes it after calling michael@0: // Run(). Be careful... michael@0: MessageLoop::current()->PostIdleTask(FROM_HERE, michael@0: new MessageLoopIdleTask(aTask, aEnsureRunsAfterMS)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsMessageLoopConstructor(nsISupports* aOuter, michael@0: const nsIID& aIID, michael@0: void** aInstancePtr) michael@0: { michael@0: if (NS_WARN_IF(aOuter)) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: nsISupports* messageLoop = new nsMessageLoop(); michael@0: return messageLoop->QueryInterface(aIID, aInstancePtr); michael@0: }