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