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