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 "RemoteLazyInputStreamThread.h"
8
9 #include "mozilla/SchedulerGroup.h"
10 #include "mozilla/StaticMutex.h"
11 #include "mozilla/StaticPtr.h"
12 #include "mozilla/TaskCategory.h"
13 #include "mozilla/ipc/BackgroundChild.h"
14 #include "mozilla/ipc/PBackgroundChild.h"
15 #include "nsXPCOMPrivate.h"
16
17 using namespace mozilla::ipc;
18
19 namespace mozilla {
20
21 namespace {
22
23 StaticMutex gRemoteLazyThreadMutex;
24 StaticRefPtr<RemoteLazyInputStreamThread> gRemoteLazyThread;
25 bool gShutdownHasStarted = false;
26
27 class ThreadInitializeRunnable final : public Runnable {
28 public:
ThreadInitializeRunnable()29 ThreadInitializeRunnable() : Runnable("dom::ThreadInitializeRunnable") {}
30
31 NS_IMETHOD
Run()32 Run() override {
33 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
34 MOZ_ASSERT(gRemoteLazyThread);
35 gRemoteLazyThread->InitializeOnMainThread();
36 return NS_OK;
37 }
38 };
39
40 class MigrateActorRunnable final : public Runnable {
41 public:
MigrateActorRunnable(RemoteLazyInputStreamChild * aActor)42 explicit MigrateActorRunnable(RemoteLazyInputStreamChild* aActor)
43 : Runnable("dom::MigrateActorRunnable"), mActor(aActor) {
44 MOZ_ASSERT(mActor);
45 }
46
47 NS_IMETHOD
Run()48 Run() override {
49 MOZ_ASSERT(mActor->State() ==
50 RemoteLazyInputStreamChild::eInactiveMigrating);
51
52 PBackgroundChild* actorChild =
53 BackgroundChild::GetOrCreateForCurrentThread();
54 if (!actorChild) {
55 return NS_OK;
56 }
57
58 if (actorChild->SendPRemoteLazyInputStreamConstructor(mActor, mActor->ID(),
59 mActor->Size())) {
60 mActor->Migrated();
61 }
62
63 return NS_OK;
64 }
65
66 private:
67 ~MigrateActorRunnable() = default;
68
69 RefPtr<RemoteLazyInputStreamChild> mActor;
70 };
71
72 } // namespace
73
NS_IMPL_ISUPPORTS(RemoteLazyInputStreamThread,nsIObserver,nsIEventTarget)74 NS_IMPL_ISUPPORTS(RemoteLazyInputStreamThread, nsIObserver, nsIEventTarget)
75
76 /* static */
77 bool RemoteLazyInputStreamThread::IsOnFileEventTarget(
78 nsIEventTarget* aEventTarget) {
79 MOZ_ASSERT(aEventTarget);
80
81 // Note that we don't migrate actors when we are on the socket process
82 // because, on that process, we don't have complex life-time contexts such
83 // as workers and documents.
84 if (XRE_IsSocketProcess()) {
85 return true;
86 }
87
88 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
89 return gRemoteLazyThread && aEventTarget == gRemoteLazyThread->mThread;
90 }
91
92 /* static */
Get()93 RemoteLazyInputStreamThread* RemoteLazyInputStreamThread::Get() {
94 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
95
96 if (gShutdownHasStarted) {
97 return nullptr;
98 }
99
100 return gRemoteLazyThread;
101 }
102
103 /* static */
GetOrCreate()104 RemoteLazyInputStreamThread* RemoteLazyInputStreamThread::GetOrCreate() {
105 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
106
107 if (gShutdownHasStarted) {
108 return nullptr;
109 }
110
111 if (!gRemoteLazyThread) {
112 gRemoteLazyThread = new RemoteLazyInputStreamThread();
113 if (!gRemoteLazyThread->Initialize()) {
114 return nullptr;
115 }
116 }
117
118 return gRemoteLazyThread;
119 }
120
Initialize()121 bool RemoteLazyInputStreamThread::Initialize() {
122 nsCOMPtr<nsIThread> thread;
123 nsresult rv = NS_NewNamedThread("RemoteLzyStream", getter_AddRefs(thread));
124 if (NS_WARN_IF(NS_FAILED(rv))) {
125 return false;
126 }
127
128 mThread = thread;
129
130 if (!mPendingActors.IsEmpty()) {
131 for (uint32_t i = 0; i < mPendingActors.Length(); ++i) {
132 MigrateActorInternal(mPendingActors[i]);
133 }
134
135 mPendingActors.Clear();
136 }
137
138 if (!NS_IsMainThread()) {
139 RefPtr<Runnable> runnable = new ThreadInitializeRunnable();
140 SchedulerGroup::Dispatch(TaskCategory::Other, runnable.forget());
141 return true;
142 }
143
144 InitializeOnMainThread();
145 return true;
146 }
147
InitializeOnMainThread()148 void RemoteLazyInputStreamThread::InitializeOnMainThread() {
149 MOZ_ASSERT(NS_IsMainThread());
150
151 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
152 if (NS_WARN_IF(!obs)) {
153 return;
154 }
155
156 nsresult rv =
157 obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
158 if (NS_WARN_IF(NS_FAILED(rv))) {
159 return;
160 }
161 }
162
163 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)164 RemoteLazyInputStreamThread::Observe(nsISupports* aSubject, const char* aTopic,
165 const char16_t* aData) {
166 MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
167
168 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
169
170 if (mThread) {
171 mThread->Shutdown();
172 mThread = nullptr;
173 }
174
175 gShutdownHasStarted = true;
176 gRemoteLazyThread = nullptr;
177
178 return NS_OK;
179 }
180
MigrateActor(RemoteLazyInputStreamChild * aActor)181 void RemoteLazyInputStreamThread::MigrateActor(
182 RemoteLazyInputStreamChild* aActor) {
183 MOZ_ASSERT(aActor->State() == RemoteLazyInputStreamChild::eInactiveMigrating);
184
185 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
186
187 if (gShutdownHasStarted) {
188 return;
189 }
190
191 if (!mThread) {
192 // The thread is not initialized yet.
193 mPendingActors.AppendElement(aActor);
194 return;
195 }
196
197 MigrateActorInternal(aActor);
198 }
199
MigrateActorInternal(RemoteLazyInputStreamChild * aActor)200 void RemoteLazyInputStreamThread::MigrateActorInternal(
201 RemoteLazyInputStreamChild* aActor) {
202 RefPtr<Runnable> runnable = new MigrateActorRunnable(aActor);
203 mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
204 }
205
206 // nsIEventTarget
207
NS_IMETHODIMP_(bool)208 NS_IMETHODIMP_(bool)
209 RemoteLazyInputStreamThread::IsOnCurrentThreadInfallible() {
210 return mThread->IsOnCurrentThread();
211 }
212
213 NS_IMETHODIMP
IsOnCurrentThread(bool * aRetval)214 RemoteLazyInputStreamThread::IsOnCurrentThread(bool* aRetval) {
215 return mThread->IsOnCurrentThread(aRetval);
216 }
217
218 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> aRunnable,uint32_t aFlags)219 RemoteLazyInputStreamThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
220 uint32_t aFlags) {
221 nsCOMPtr<nsIRunnable> runnable(aRunnable);
222
223 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
224
225 if (gShutdownHasStarted) {
226 return NS_ERROR_NOT_INITIALIZED;
227 }
228
229 return mThread->Dispatch(runnable.forget(), aFlags);
230 }
231
232 NS_IMETHODIMP
DispatchFromScript(nsIRunnable * aRunnable,uint32_t aFlags)233 RemoteLazyInputStreamThread::DispatchFromScript(nsIRunnable* aRunnable,
234 uint32_t aFlags) {
235 nsCOMPtr<nsIRunnable> runnable(aRunnable);
236 return Dispatch(runnable.forget(), aFlags);
237 }
238
239 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable>,uint32_t)240 RemoteLazyInputStreamThread::DelayedDispatch(already_AddRefed<nsIRunnable>,
241 uint32_t) {
242 return NS_ERROR_NOT_IMPLEMENTED;
243 }
244
IsOnDOMFileThread()245 bool IsOnDOMFileThread() {
246 StaticMutexAutoLock lock(gRemoteLazyThreadMutex);
247
248 MOZ_ASSERT(!gShutdownHasStarted);
249 MOZ_ASSERT(gRemoteLazyThread);
250
251 return gRemoteLazyThread->IsOnCurrentThreadInfallible();
252 }
253
AssertIsOnDOMFileThread()254 void AssertIsOnDOMFileThread() { MOZ_ASSERT(IsOnDOMFileThread()); }
255
256 } // namespace mozilla
257