Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsMessageLoop.h"
8 : #include "mozilla/WeakPtr.h"
9 : #include "base/message_loop.h"
10 : #include "base/task.h"
11 : #include "nsIRunnable.h"
12 : #include "nsITimer.h"
13 : #include "nsCOMPtr.h"
14 : #include "nsAutoPtr.h"
15 : #include "nsComponentManagerUtils.h"
16 : #include "nsThreadUtils.h"
17 :
18 : using namespace mozilla;
19 :
20 : namespace {
21 :
22 : /**
23 : * This Task runs its nsIRunnable when Run() is called, or after
24 : * aEnsureRunsAfterMS milliseconds have elapsed since the object was
25 : * constructed.
26 : *
27 : * Note that the MessageLoop owns this object and will delete it after it calls
28 : * Run(). Tread lightly.
29 : */
30 : class MessageLoopIdleTask
31 : : public Runnable
32 : , public SupportsWeakPtr<MessageLoopIdleTask>
33 : {
34 : public:
35 0 : MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MessageLoopIdleTask)
36 : MessageLoopIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS);
37 : NS_IMETHOD Run() override;
38 :
39 : private:
40 : nsresult Init(uint32_t aEnsureRunsAfterMS);
41 :
42 : nsCOMPtr<nsIRunnable> mTask;
43 : nsCOMPtr<nsITimer> mTimer;
44 :
45 0 : virtual ~MessageLoopIdleTask() {}
46 : };
47 :
48 : /**
49 : * This timer callback calls MessageLoopIdleTask::Run() when its timer fires.
50 : * (The timer can't call back into MessageLoopIdleTask directly since that's
51 : * not a refcounted object; it's owned by the MessageLoop.)
52 : *
53 : * We keep a weak reference to the MessageLoopIdleTask, although a raw pointer
54 : * should in theory suffice: When the MessageLoopIdleTask runs (right before
55 : * the MessageLoop deletes it), it cancels its timer. But the weak pointer
56 : * saves us from worrying about an edge case somehow messing us up here.
57 : */
58 : class MessageLoopTimerCallback
59 : : public nsITimerCallback
60 : {
61 : public:
62 : explicit MessageLoopTimerCallback(MessageLoopIdleTask* aTask);
63 :
64 : NS_DECL_ISUPPORTS
65 : NS_DECL_NSITIMERCALLBACK
66 :
67 : private:
68 : WeakPtr<MessageLoopIdleTask> mTask;
69 :
70 0 : virtual ~MessageLoopTimerCallback() {}
71 : };
72 :
73 0 : MessageLoopIdleTask::MessageLoopIdleTask(nsIRunnable* aTask,
74 0 : uint32_t aEnsureRunsAfterMS)
75 : : mozilla::Runnable("MessageLoopIdleTask")
76 0 : , mTask(aTask)
77 : {
78 : // Init() really shouldn't fail, but if it does, we schedule our runnable
79 : // immediately, because it's more important to guarantee that we run the task
80 : // eventually than it is to run the task when we're idle.
81 0 : nsresult rv = Init(aEnsureRunsAfterMS);
82 0 : if (NS_FAILED(rv)) {
83 0 : NS_WARNING("Running idle task early because we couldn't initialize our timer.");
84 0 : NS_DispatchToCurrentThread(mTask);
85 :
86 0 : mTask = nullptr;
87 0 : mTimer = nullptr;
88 : }
89 0 : }
90 :
91 : nsresult
92 0 : MessageLoopIdleTask::Init(uint32_t aEnsureRunsAfterMS)
93 : {
94 0 : mTimer = do_CreateInstance("@mozilla.org/timer;1");
95 0 : if (NS_WARN_IF(!mTimer)) {
96 0 : return NS_ERROR_UNEXPECTED;
97 : }
98 :
99 : RefPtr<MessageLoopTimerCallback> callback =
100 0 : new MessageLoopTimerCallback(this);
101 :
102 0 : return mTimer->InitWithCallback(callback, aEnsureRunsAfterMS,
103 0 : nsITimer::TYPE_ONE_SHOT);
104 : }
105 :
106 : NS_IMETHODIMP
107 0 : MessageLoopIdleTask::Run()
108 : {
109 : // Null out our pointers because if Run() was called by the timer, this
110 : // object will be kept alive by the MessageLoop until the MessageLoop calls
111 : // Run().
112 :
113 0 : if (mTimer) {
114 0 : mTimer->Cancel();
115 0 : mTimer = nullptr;
116 : }
117 :
118 0 : if (mTask) {
119 0 : mTask->Run();
120 0 : mTask = nullptr;
121 : }
122 :
123 0 : return NS_OK;
124 : }
125 :
126 0 : MessageLoopTimerCallback::MessageLoopTimerCallback(MessageLoopIdleTask* aTask)
127 0 : : mTask(aTask)
128 : {
129 0 : }
130 :
131 : NS_IMETHODIMP
132 0 : MessageLoopTimerCallback::Notify(nsITimer* aTimer)
133 : {
134 : // We don't expect to hit the case when the timer fires but mTask has been
135 : // deleted, because mTask should cancel the timer before the mTask is
136 : // deleted. But you never know...
137 0 : NS_WARNING_ASSERTION(mTask, "This timer shouldn't have fired.");
138 :
139 0 : if (mTask) {
140 0 : mTask->Run();
141 : }
142 0 : return NS_OK;
143 : }
144 :
145 0 : NS_IMPL_ISUPPORTS(MessageLoopTimerCallback, nsITimerCallback)
146 :
147 : } // namespace
148 :
149 0 : NS_IMPL_ISUPPORTS(nsMessageLoop, nsIMessageLoop)
150 :
151 : NS_IMETHODIMP
152 0 : nsMessageLoop::PostIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS)
153 : {
154 : // The message loop owns MessageLoopIdleTask and deletes it after calling
155 : // Run(). Be careful...
156 : RefPtr<MessageLoopIdleTask> idle =
157 0 : new MessageLoopIdleTask(aTask, aEnsureRunsAfterMS);
158 0 : MessageLoop::current()->PostIdleTask(idle.forget());
159 :
160 0 : return NS_OK;
161 : }
162 :
163 : nsresult
164 0 : nsMessageLoopConstructor(nsISupports* aOuter,
165 : const nsIID& aIID,
166 : void** aInstancePtr)
167 : {
168 0 : if (NS_WARN_IF(aOuter)) {
169 0 : return NS_ERROR_NO_AGGREGATION;
170 : }
171 0 : nsISupports* messageLoop = new nsMessageLoop();
172 0 : return messageLoop->QueryInterface(aIID, aInstancePtr);
173 : }
|