fca606ec233ff34196de9dc0d415b39a82ad024b
[WebKit-https.git] / Source / WebCore / workers / WorkerThread.cpp
1 /*
2  * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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
29 #include "WorkerThread.h"
30
31 #include "DedicatedWorkerGlobalScope.h"
32 #include "InspectorInstrumentation.h"
33 #include "ScriptSourceCode.h"
34 #include "SecurityOrigin.h"
35 #include "ThreadGlobalData.h"
36 #include "URL.h"
37 #include <utility>
38 #include <wtf/NeverDestroyed.h>
39 #include <wtf/Noncopyable.h>
40 #include <wtf/text/WTFString.h>
41
42 #if ENABLE(SQL_DATABASE)
43 #include "DatabaseManager.h"
44 #include "DatabaseTask.h"
45 #endif
46
47 #if PLATFORM(IOS)
48 #include "WebCoreThread.h"
49 #endif
50
51 namespace WebCore {
52
53 static std::mutex& threadSetMutex()
54 {
55     static std::once_flag onceFlag;
56     static std::mutex* mutex;
57
58     std::call_once(onceFlag, []{
59         mutex = std::make_unique<std::mutex>().release();
60     });
61
62     return *mutex;
63 }
64
65 static HashSet<WorkerThread*>& workerThreads()
66 {
67     static NeverDestroyed<HashSet<WorkerThread*>> workerThreads;
68
69     return workerThreads;
70 }
71
72 unsigned WorkerThread::workerThreadCount()
73 {
74     std::lock_guard<std::mutex> lock(threadSetMutex());
75
76     return workerThreads().size();
77 }
78
79 struct WorkerThreadStartupData {
80     WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED;
81 public:
82     static PassOwnPtr<WorkerThreadStartupData> create(const URL& scriptURL, const String& userAgent, const GroupSettings* settings, const String& sourceCode, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
83     {
84         return adoptPtr(new WorkerThreadStartupData(scriptURL, userAgent, settings, sourceCode, startMode, contentSecurityPolicy, contentSecurityPolicyType, topOrigin));
85     }
86
87     URL m_scriptURL;
88     String m_userAgent;
89     std::unique_ptr<GroupSettings> m_groupSettings;
90     String m_sourceCode;
91     WorkerThreadStartMode m_startMode;
92     String m_contentSecurityPolicy;
93     ContentSecurityPolicy::HeaderType m_contentSecurityPolicyType;
94     RefPtr<SecurityOrigin> m_topOrigin;
95 private:
96     WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const GroupSettings*, const String& sourceCode, WorkerThreadStartMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin);
97 };
98
99 WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const GroupSettings* settings, const String& sourceCode, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
100     : m_scriptURL(scriptURL.copy())
101     , m_userAgent(userAgent.isolatedCopy())
102     , m_sourceCode(sourceCode.isolatedCopy())
103     , m_startMode(startMode)
104     , m_contentSecurityPolicy(contentSecurityPolicy.isolatedCopy())
105     , m_contentSecurityPolicyType(contentSecurityPolicyType)
106     , m_topOrigin(topOrigin ? topOrigin->isolatedCopy() : 0)
107 {
108     if (!settings)
109         return;
110
111     m_groupSettings = std::make_unique<GroupSettings>();
112     m_groupSettings->setLocalStorageQuotaBytes(settings->localStorageQuotaBytes());
113     m_groupSettings->setIndexedDBQuotaBytes(settings->indexedDBQuotaBytes());
114     m_groupSettings->setIndexedDBDatabasePath(settings->indexedDBDatabasePath().isolatedCopy());
115 }
116
117 WorkerThread::WorkerThread(const URL& scriptURL, const String& userAgent, const GroupSettings* settings, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
118     : m_threadID(0)
119     , m_workerLoaderProxy(workerLoaderProxy)
120     , m_workerReportingProxy(workerReportingProxy)
121     , m_startupData(WorkerThreadStartupData::create(scriptURL, userAgent, settings, sourceCode, startMode, contentSecurityPolicy, contentSecurityPolicyType, topOrigin))
122 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
123     , m_notificationClient(0)
124 #endif
125 {
126     std::lock_guard<std::mutex> lock(threadSetMutex());
127
128     workerThreads().add(this);
129 }
130
131 WorkerThread::~WorkerThread()
132 {
133     std::lock_guard<std::mutex> lock(threadSetMutex());
134
135     ASSERT(workerThreads().contains(this));
136     workerThreads().remove(this);
137 }
138
139 bool WorkerThread::start()
140 {
141     // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts.
142     MutexLocker lock(m_threadCreationMutex);
143
144     if (m_threadID)
145         return true;
146
147     m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker");
148
149     return m_threadID;
150 }
151
152 void WorkerThread::workerThreadStart(void* thread)
153 {
154     static_cast<WorkerThread*>(thread)->workerThread();
155 }
156
157 void WorkerThread::workerThread()
158 {
159     // Propagate the mainThread's fenv to workers.
160 #if PLATFORM(IOS)
161     fesetenv(&mainThreadFEnv);
162 #endif
163
164     {
165         MutexLocker lock(m_threadCreationMutex);
166         m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_userAgent, std::move(m_startupData->m_groupSettings), m_startupData->m_contentSecurityPolicy, m_startupData->m_contentSecurityPolicyType, m_startupData->m_topOrigin.release());
167
168         if (m_runLoop.terminated()) {
169             // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
170             // forbidExecution() couldn't be called from stop().
171             m_workerGlobalScope->script()->forbidExecution();
172         }
173     }
174
175     WorkerScriptController* script = m_workerGlobalScope->script();
176 #if ENABLE(INSPECTOR)
177     InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), m_startupData->m_startMode);
178 #endif
179     script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL));
180     // Free the startup data to cause its member variable deref's happen on the worker's thread (since
181     // all ref/derefs of these objects are happening on the thread at this point). Note that
182     // WorkerThread::~WorkerThread happens on a different thread where it was created.
183     m_startupData.clear();
184
185     runEventLoop();
186
187     ThreadIdentifier threadID = m_threadID;
188
189     ASSERT(m_workerGlobalScope->hasOneRef());
190
191     // The below assignment will destroy the context, which will in turn notify messaging proxy.
192     // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
193     m_workerGlobalScope = 0;
194
195     // Clean up WebCore::ThreadGlobalData before WTF::WTFThreadData goes away!
196     threadGlobalData().destroy();
197
198     // The thread object may be already destroyed from notification now, don't try to access "this".
199     detachThread(threadID);
200 }
201
202 void WorkerThread::runEventLoop()
203 {
204     // Does not return until terminated.
205     m_runLoop.run(m_workerGlobalScope.get());
206 }
207
208 class WorkerThreadShutdownFinishTask : public ScriptExecutionContext::Task {
209 public:
210     static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
211     {
212         return adoptPtr(new WorkerThreadShutdownFinishTask());
213     }
214
215     virtual void performTask(ScriptExecutionContext *context)
216     {
217         ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
218         WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
219         // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
220         workerGlobalScope->clearScript();
221     }
222
223     virtual bool isCleanupTask() const { return true; }
224 };
225
226 class WorkerThreadShutdownStartTask : public ScriptExecutionContext::Task {
227 public:
228     static PassOwnPtr<WorkerThreadShutdownStartTask> create()
229     {
230         return adoptPtr(new WorkerThreadShutdownStartTask());
231     }
232
233     virtual void performTask(ScriptExecutionContext *context)
234     {
235         ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope());
236         WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
237
238 #if ENABLE(SQL_DATABASE)
239         // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below?
240         DatabaseTaskSynchronizer cleanupSync;
241         DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync);
242 #endif
243
244         workerGlobalScope->stopActiveDOMObjects();
245
246         workerGlobalScope->notifyObserversOfStop();
247
248         // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
249         // which become dangling once Heap is destroyed.
250         workerGlobalScope->removeAllEventListeners();
251
252 #if ENABLE(SQL_DATABASE)
253         // We wait for the database thread to clean up all its stuff so that we
254         // can do more stringent leak checks as we exit.
255         cleanupSync.waitForTaskCompletion();
256 #endif
257
258         // Stick a shutdown command at the end of the queue, so that we deal
259         // with all the cleanup tasks the databases post first.
260         workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create());
261     }
262
263     virtual bool isCleanupTask() const { return true; }
264 };
265
266 void WorkerThread::stop()
267 {
268     // Mutex protection is necessary because stop() can be called before the context is fully created.
269     MutexLocker lock(m_threadCreationMutex);
270
271     // 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.
272     if (m_workerGlobalScope) {
273         m_workerGlobalScope->script()->scheduleExecutionTermination();
274
275 #if ENABLE(SQL_DATABASE)
276         DatabaseManager::manager().interruptAllDatabasesForContext(m_workerGlobalScope.get());
277 #endif
278         m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create());
279         return;
280     }
281     m_runLoop.terminate();
282 }
283
284 class ReleaseFastMallocFreeMemoryTask : public ScriptExecutionContext::Task {
285     virtual void performTask(ScriptExecutionContext*) override { WTF::releaseFastMallocFreeMemory(); }
286 };
287
288 void WorkerThread::releaseFastMallocFreeMemoryInAllThreads()
289 {
290     std::lock_guard<std::mutex> lock(threadSetMutex());
291
292     for (auto* workerThread : workerThreads())
293         workerThread->runLoop().postTask(adoptPtr(new ReleaseFastMallocFreeMemoryTask));
294 }
295
296 } // namespace WebCore