Crash in SWServerJobQueue::runNextJobSynchronously
[WebKit-https.git] / Source / WebCore / workers / WorkerThread.cpp
1 /*
2  * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26
27 #include "config.h"
28 #include "WorkerThread.h"
29
30 #include "ContentSecurityPolicyResponseHeaders.h"
31 #include "IDBConnectionProxy.h"
32 #include "ScriptSourceCode.h"
33 #include "SecurityOrigin.h"
34 #include "SocketProvider.h"
35 #include "ThreadGlobalData.h"
36 #include <wtf/URL.h>
37 #include "WorkerGlobalScope.h"
38 #include "WorkerInspectorController.h"
39 #include <utility>
40 #include <wtf/Lock.h>
41 #include <wtf/NeverDestroyed.h>
42 #include <wtf/Noncopyable.h>
43 #include <wtf/text/WTFString.h>
44
45 #if PLATFORM(IOS_FAMILY)
46 #include "FloatingPointEnvironment.h"
47 #include "WebCoreThread.h"
48 #endif
49
50 #if USE(GLIB)
51 #include <wtf/glib/GRefPtr.h>
52 #endif
53
54 namespace WebCore {
55
56 HashSet<WorkerThread*>& WorkerThread::workerThreads(const LockHolder&)
57 {
58     static NeverDestroyed<HashSet<WorkerThread*>> workerThreads;
59     return workerThreads;
60 }
61
62 Lock& WorkerThread::workerThreadsMutex()
63 {
64     static Lock mutex;
65     return mutex;
66 }
67
68 unsigned WorkerThread::workerThreadCount()
69 {
70     LockHolder lock(workerThreadsMutex());
71     return workerThreads(lock).size();
72 }
73
74 struct WorkerThreadStartupData {
75     WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED;
76 public:
77     WorkerThreadStartupData(const URL& scriptURL, const String& name, const String& identifier, const String& userAgent, bool isOnline, const String& sourceCode, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin, PAL::SessionID);
78
79     URL m_scriptURL;
80     Ref<SecurityOrigin> m_origin;
81     String m_name;
82     String m_identifier;
83     String m_userAgent;
84     String m_sourceCode;
85     WorkerThreadStartMode m_startMode;
86     ContentSecurityPolicyResponseHeaders m_contentSecurityPolicyResponseHeaders;
87     bool m_shouldBypassMainWorldContentSecurityPolicy;
88     bool m_isOnline;
89     Ref<SecurityOrigin> m_topOrigin;
90     MonotonicTime m_timeOrigin;
91     PAL::SessionID m_sessionID;
92 };
93
94 WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& name, const String& identifier, const String& userAgent, bool isOnline, const String& sourceCode, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin, PAL::SessionID sessionID)
95     : m_scriptURL(scriptURL.isolatedCopy())
96     , m_origin(SecurityOrigin::create(m_scriptURL)->isolatedCopy())
97     , m_name(name.isolatedCopy())
98     , m_identifier(identifier.isolatedCopy())
99     , m_userAgent(userAgent.isolatedCopy())
100     , m_sourceCode(sourceCode.isolatedCopy())
101     , m_startMode(startMode)
102     , m_contentSecurityPolicyResponseHeaders(contentSecurityPolicyResponseHeaders.isolatedCopy())
103     , m_shouldBypassMainWorldContentSecurityPolicy(shouldBypassMainWorldContentSecurityPolicy)
104     , m_isOnline(isOnline)
105     , m_topOrigin(topOrigin.isolatedCopy())
106     , m_timeOrigin(timeOrigin)
107     , m_sessionID(sessionID.isolatedCopy())
108 {
109 }
110
111 WorkerThread::WorkerThread(const URL& scriptURL, const String& name, const String& identifier, const String& userAgent, bool isOnline, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerDebuggerProxy& workerDebuggerProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags, PAL::SessionID sessionID)
112     : m_identifier(identifier.isolatedCopy())
113     , m_workerLoaderProxy(workerLoaderProxy)
114     , m_workerDebuggerProxy(workerDebuggerProxy)
115     , m_workerReportingProxy(workerReportingProxy)
116     , m_runtimeFlags(runtimeFlags)
117     , m_startupData(std::make_unique<WorkerThreadStartupData>(scriptURL, name, identifier, userAgent, isOnline, sourceCode, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin, timeOrigin, sessionID))
118 #if ENABLE(INDEXED_DATABASE)
119     , m_idbConnectionProxy(connectionProxy)
120 #endif
121     , m_socketProvider(socketProvider)
122 {
123 #if !ENABLE(INDEXED_DATABASE)
124     UNUSED_PARAM(connectionProxy);
125 #endif
126
127     LockHolder lock(workerThreadsMutex());
128     workerThreads(lock).add(this);
129 }
130
131 WorkerThread::~WorkerThread()
132 {
133     LockHolder lock(workerThreadsMutex());
134     ASSERT(workerThreads(lock).contains(this));
135     workerThreads(lock).remove(this);
136 }
137
138 void WorkerThread::start(WTF::Function<void(const String&)>&& evaluateCallback)
139 {
140     // Mutex protection is necessary to ensure that m_thread is initialized when the thread starts.
141     LockHolder lock(m_threadCreationAndWorkerGlobalScopeMutex);
142
143     if (m_thread)
144         return;
145
146     m_evaluateCallback = WTFMove(evaluateCallback);
147
148     m_thread = Thread::create(isServiceWorkerThread() ? "WebCore: Service Worker" : "WebCore: Worker", [this] {
149         workerThread();
150     });
151 }
152
153 void WorkerThread::workerThread()
154 {
155     auto protectedThis = makeRef(*this);
156
157     // Propagate the mainThread's fenv to workers.
158 #if PLATFORM(IOS_FAMILY)
159     FloatingPointEnvironment::singleton().propagateMainThreadEnvironment();
160 #endif
161
162 #if USE(GLIB)
163     GRefPtr<GMainContext> mainContext = adoptGRef(g_main_context_new());
164     g_main_context_push_thread_default(mainContext.get());
165 #endif
166
167     WorkerScriptController* scriptController;
168     {
169         // Mutex protection is necessary to ensure that we don't change m_workerGlobalScope
170         // while WorkerThread::stop() is accessing it. Note that WorkerThread::stop() can
171         // be called before we've finished creating the WorkerGlobalScope.
172         LockHolder lock(m_threadCreationAndWorkerGlobalScopeMutex);
173         m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, WTFMove(m_startupData->m_origin), m_startupData->m_name, m_startupData->m_identifier, m_startupData->m_userAgent, m_startupData->m_isOnline, m_startupData->m_contentSecurityPolicyResponseHeaders, m_startupData->m_shouldBypassMainWorldContentSecurityPolicy, WTFMove(m_startupData->m_topOrigin), m_startupData->m_timeOrigin, m_startupData->m_sessionID);
174
175         scriptController = m_workerGlobalScope->script();
176
177         if (m_runLoop.terminated()) {
178             // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
179             // forbidExecution() couldn't be called from stop().
180             scriptController->scheduleExecutionTermination();
181             scriptController->forbidExecution();
182         }
183     }
184
185     if (m_startupData->m_startMode == WorkerThreadStartMode::WaitForInspector) {
186         startRunningDebuggerTasks();
187
188         // If the worker was somehow terminated while processing debugger commands.
189         if (m_runLoop.terminated())
190             scriptController->forbidExecution();
191     }
192
193     String exceptionMessage;
194     scriptController->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, URL(m_startupData->m_scriptURL)), &exceptionMessage);
195
196     callOnMainThread([evaluateCallback = WTFMove(m_evaluateCallback), message = exceptionMessage.isolatedCopy()] {
197         if (evaluateCallback)
198             evaluateCallback(message);
199     });
200
201     // Free the startup data to cause its member variable deref's happen on the worker's thread (since
202     // all ref/derefs of these objects are happening on the thread at this point). Note that
203     // WorkerThread::~WorkerThread happens on a different thread where it was created.
204     m_startupData = nullptr;
205
206     runEventLoop();
207
208 #if USE(GLIB)
209     g_main_context_pop_thread_default(mainContext.get());
210 #endif
211
212     RefPtr<Thread> protector = m_thread;
213
214     ASSERT(m_workerGlobalScope->hasOneRef());
215
216     RefPtr<WorkerGlobalScope> workerGlobalScopeToDelete;
217     {
218         // Mutex protection is necessary to ensure that we don't change m_workerGlobalScope
219         // while WorkerThread::stop is accessing it.
220         LockHolder lock(m_threadCreationAndWorkerGlobalScopeMutex);
221
222         // Delay the destruction of the WorkerGlobalScope context until after we've unlocked the
223         // m_threadCreationAndWorkerGlobalScopeMutex. This is needed because destructing the
224         // context will trigger the main thread to race against us to delete the WorkerThread
225         // object, and the WorkerThread object owns the mutex we need to unlock after this.
226         workerGlobalScopeToDelete = WTFMove(m_workerGlobalScope);
227
228         if (m_stoppedCallback)
229             callOnMainThread(WTFMove(m_stoppedCallback));
230     }
231
232     // The below assignment will destroy the context, which will in turn notify messaging proxy.
233     // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
234     workerGlobalScopeToDelete = nullptr;
235
236     // Clean up WebCore::ThreadGlobalData before WTF::Thread goes away!
237     threadGlobalData().destroy();
238
239     // Send the last WorkerThread Ref to be Deref'ed on the main thread.
240     callOnMainThread([protectedThis = WTFMove(protectedThis)] { });
241
242     // The thread object may be already destroyed from notification now, don't try to access "this".
243     protector->detach();
244 }
245
246 void WorkerThread::startRunningDebuggerTasks()
247 {
248     ASSERT(!m_pausedForDebugger);
249     m_pausedForDebugger = true;
250
251     MessageQueueWaitResult result;
252     do {
253         result = m_runLoop.runInDebuggerMode(*m_workerGlobalScope);
254     } while (result != MessageQueueTerminated && m_pausedForDebugger);
255 }
256
257 void WorkerThread::stopRunningDebuggerTasks()
258 {
259     m_pausedForDebugger = false;
260 }
261
262 void WorkerThread::runEventLoop()
263 {
264     // Does not return until terminated.
265     m_runLoop.run(m_workerGlobalScope.get());
266 }
267
268 void WorkerThread::stop(WTF::Function<void()>&& stoppedCallback)
269 {
270     // Mutex protection is necessary to ensure that m_workerGlobalScope isn't changed by
271     // WorkerThread::workerThread() while we're accessing it. Note also that stop() can
272     // be called before m_workerGlobalScope is fully created.
273     auto locker = Locker<Lock>::tryLock(m_threadCreationAndWorkerGlobalScopeMutex);
274     if (!locker) {
275         // The thread is still starting, spin the runloop and try again to avoid deadlocks if the worker thread
276         // needs to interact with the main thread during startup.
277         callOnMainThread([this, stoppedCallback = WTFMove(stoppedCallback)]() mutable {
278             stop(WTFMove(stoppedCallback));
279         });
280         return;
281     }
282
283     ASSERT(!m_stoppedCallback);
284     m_stoppedCallback = WTFMove(stoppedCallback);
285
286     // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever.
287     if (m_workerGlobalScope) {
288         m_workerGlobalScope->script()->scheduleExecutionTermination();
289
290         m_runLoop.postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context ) {
291             WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
292
293             workerGlobalScope.prepareForTermination();
294
295             // Stick a shutdown command at the end of the queue, so that we deal
296             // with all the cleanup tasks the databases post first.
297             workerGlobalScope.postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) {
298                 WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
299                 // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
300                 workerGlobalScope.clearScript();
301             } });
302
303         } });
304         return;
305     }
306     m_runLoop.terminate();
307 }
308
309 void WorkerThread::releaseFastMallocFreeMemoryInAllThreads()
310 {
311     LockHolder lock(workerThreadsMutex());
312     for (auto* workerThread : workerThreads(lock)) {
313         workerThread->runLoop().postTask([] (ScriptExecutionContext&) {
314             WTF::releaseFastMallocFreeMemory();
315         });
316     }
317 }
318
319 IDBClient::IDBConnectionProxy* WorkerThread::idbConnectionProxy()
320 {
321 #if ENABLE(INDEXED_DATABASE)
322     return m_idbConnectionProxy.get();
323 #else
324     return nullptr;
325 #endif
326 }
327
328 SocketProvider* WorkerThread::socketProvider()
329 {
330     return m_socketProvider.get();
331 }
332
333 } // namespace WebCore