diff options
Diffstat (limited to 'Source/WebCore/workers/WorkerThread.cpp')
-rw-r--r-- | Source/WebCore/workers/WorkerThread.cpp | 258 |
1 files changed, 131 insertions, 127 deletions
diff --git a/Source/WebCore/workers/WorkerThread.cpp b/Source/WebCore/workers/WorkerThread.cpp index fca606ec2..8b1e9234b 100644 --- a/Source/WebCore/workers/WorkerThread.cpp +++ b/Source/WebCore/workers/WorkerThread.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,42 +25,35 @@ */ #include "config.h" - #include "WorkerThread.h" -#include "DedicatedWorkerGlobalScope.h" -#include "InspectorInstrumentation.h" +#include "ContentSecurityPolicyResponseHeaders.h" +#include "IDBConnectionProxy.h" #include "ScriptSourceCode.h" #include "SecurityOrigin.h" +#include "SocketProvider.h" #include "ThreadGlobalData.h" #include "URL.h" +#include "WorkerGlobalScope.h" +#include "WorkerInspectorController.h" #include <utility> +#include <wtf/Lock.h> #include <wtf/NeverDestroyed.h> #include <wtf/Noncopyable.h> #include <wtf/text/WTFString.h> -#if ENABLE(SQL_DATABASE) -#include "DatabaseManager.h" -#include "DatabaseTask.h" -#endif - #if PLATFORM(IOS) +#include "FloatingPointEnvironment.h" #include "WebCoreThread.h" #endif -namespace WebCore { - -static std::mutex& threadSetMutex() -{ - static std::once_flag onceFlag; - static std::mutex* mutex; +#if PLATFORM(GTK) +#include <wtf/glib/GRefPtr.h> +#endif - std::call_once(onceFlag, []{ - mutex = std::make_unique<std::mutex>().release(); - }); +namespace WebCore { - return *mutex; -} +static StaticLock threadSetMutex; static HashSet<WorkerThread*>& workerThreads() { @@ -71,7 +64,7 @@ static HashSet<WorkerThread*>& workerThreads() unsigned WorkerThread::workerThreadCount() { - std::lock_guard<std::mutex> lock(threadSetMutex()); + std::lock_guard<StaticLock> lock(threadSetMutex); return workerThreads().size(); } @@ -79,58 +72,60 @@ unsigned WorkerThread::workerThreadCount() struct WorkerThreadStartupData { WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED; public: - 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) - { - return adoptPtr(new WorkerThreadStartupData(scriptURL, userAgent, settings, sourceCode, startMode, contentSecurityPolicy, contentSecurityPolicyType, topOrigin)); - } + WorkerThreadStartupData(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerThreadStartMode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin); URL m_scriptURL; + String m_identifier; String m_userAgent; - std::unique_ptr<GroupSettings> m_groupSettings; String m_sourceCode; WorkerThreadStartMode m_startMode; - String m_contentSecurityPolicy; - ContentSecurityPolicy::HeaderType m_contentSecurityPolicyType; - RefPtr<SecurityOrigin> m_topOrigin; -private: - WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const GroupSettings*, const String& sourceCode, WorkerThreadStartMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin); + ContentSecurityPolicyResponseHeaders m_contentSecurityPolicyResponseHeaders; + bool m_shouldBypassMainWorldContentSecurityPolicy; + Ref<SecurityOrigin> m_topOrigin; + MonotonicTime m_timeOrigin; }; -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) - : m_scriptURL(scriptURL.copy()) +WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin) + : m_scriptURL(scriptURL.isolatedCopy()) + , m_identifier(identifier.isolatedCopy()) , m_userAgent(userAgent.isolatedCopy()) , m_sourceCode(sourceCode.isolatedCopy()) , m_startMode(startMode) - , m_contentSecurityPolicy(contentSecurityPolicy.isolatedCopy()) - , m_contentSecurityPolicyType(contentSecurityPolicyType) - , m_topOrigin(topOrigin ? topOrigin->isolatedCopy() : 0) + , m_contentSecurityPolicyResponseHeaders(contentSecurityPolicyResponseHeaders.isolatedCopy()) + , m_shouldBypassMainWorldContentSecurityPolicy(shouldBypassMainWorldContentSecurityPolicy) + , m_topOrigin(topOrigin.isolatedCopy()) + , m_timeOrigin(timeOrigin) { - if (!settings) - return; - - m_groupSettings = std::make_unique<GroupSettings>(); - m_groupSettings->setLocalStorageQuotaBytes(settings->localStorageQuotaBytes()); - m_groupSettings->setIndexedDBQuotaBytes(settings->indexedDBQuotaBytes()); - m_groupSettings->setIndexedDBDatabasePath(settings->indexedDBDatabasePath().isolatedCopy()); } -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) +WorkerThread::WorkerThread(const URL& scriptURL, const String& identifier, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const SecurityOrigin& topOrigin, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags) : m_threadID(0) , m_workerLoaderProxy(workerLoaderProxy) , m_workerReportingProxy(workerReportingProxy) - , m_startupData(WorkerThreadStartupData::create(scriptURL, userAgent, settings, sourceCode, startMode, contentSecurityPolicy, contentSecurityPolicyType, topOrigin)) -#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) - , m_notificationClient(0) + , m_runtimeFlags(runtimeFlags) + , m_startupData(std::make_unique<WorkerThreadStartupData>(scriptURL, identifier, userAgent, sourceCode, startMode, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, topOrigin, timeOrigin)) +#if ENABLE(INDEXED_DATABASE) + , m_idbConnectionProxy(connectionProxy) +#endif +#if ENABLE(WEB_SOCKETS) + , m_socketProvider(socketProvider) #endif { - std::lock_guard<std::mutex> lock(threadSetMutex()); +#if !ENABLE(INDEXED_DATABASE) + UNUSED_PARAM(connectionProxy); +#endif +#if !ENABLE(WEB_SOCKETS) + UNUSED_PARAM(socketProvider); +#endif + + std::lock_guard<StaticLock> lock(threadSetMutex); workerThreads().add(this); } WorkerThread::~WorkerThread() { - std::lock_guard<std::mutex> lock(threadSetMutex()); + std::lock_guard<StaticLock> lock(threadSetMutex); ASSERT(workerThreads().contains(this)); workerThreads().remove(this); @@ -139,7 +134,7 @@ WorkerThread::~WorkerThread() bool WorkerThread::start() { // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts. - MutexLocker lock(m_threadCreationMutex); + LockHolder lock(m_threadCreationMutex); if (m_threadID) return true; @@ -158,12 +153,17 @@ void WorkerThread::workerThread() { // Propagate the mainThread's fenv to workers. #if PLATFORM(IOS) - fesetenv(&mainThreadFEnv); + FloatingPointEnvironment::singleton().propagateMainThreadEnvironment(); +#endif + +#if PLATFORM(GTK) + GRefPtr<GMainContext> mainContext = adoptGRef(g_main_context_new()); + g_main_context_push_thread_default(mainContext.get()); #endif { - MutexLocker lock(m_threadCreationMutex); - 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()); + LockHolder lock(m_threadCreationMutex); + m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_identifier, m_startupData->m_userAgent, m_startupData->m_contentSecurityPolicyResponseHeaders, m_startupData->m_shouldBypassMainWorldContentSecurityPolicy, WTFMove(m_startupData->m_topOrigin), m_startupData->m_timeOrigin); if (m_runLoop.terminated()) { // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet, @@ -172,25 +172,34 @@ void WorkerThread::workerThread() } } + if (m_startupData->m_startMode == WorkerThreadStartMode::WaitForInspector) { + startRunningDebuggerTasks(); + + // If the worker was somehow terminated while processing debugger commands. + if (m_runLoop.terminated()) + m_workerGlobalScope->script()->forbidExecution(); + } + WorkerScriptController* script = m_workerGlobalScope->script(); -#if ENABLE(INSPECTOR) - InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), m_startupData->m_startMode); -#endif script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL)); // Free the startup data to cause its member variable deref's happen on the worker's thread (since // all ref/derefs of these objects are happening on the thread at this point). Note that // WorkerThread::~WorkerThread happens on a different thread where it was created. - m_startupData.clear(); + m_startupData = nullptr; runEventLoop(); +#if PLATFORM(GTK) + g_main_context_pop_thread_default(mainContext.get()); +#endif + ThreadIdentifier threadID = m_threadID; ASSERT(m_workerGlobalScope->hasOneRef()); // The below assignment will destroy the context, which will in turn notify messaging proxy. // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. - m_workerGlobalScope = 0; + m_workerGlobalScope = nullptr; // Clean up WebCore::ThreadGlobalData before WTF::WTFThreadData goes away! threadGlobalData().destroy(); @@ -199,98 +208,93 @@ void WorkerThread::workerThread() detachThread(threadID); } +void WorkerThread::startRunningDebuggerTasks() +{ + ASSERT(!m_pausedForDebugger); + m_pausedForDebugger = true; + + MessageQueueWaitResult result; + do { + result = m_runLoop.runInMode(m_workerGlobalScope.get(), WorkerRunLoop::debuggerMode()); + } while (result != MessageQueueTerminated && m_pausedForDebugger); +} + +void WorkerThread::stopRunningDebuggerTasks() +{ + m_pausedForDebugger = false; +} + void WorkerThread::runEventLoop() { // Does not return until terminated. m_runLoop.run(m_workerGlobalScope.get()); } -class WorkerThreadShutdownFinishTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<WorkerThreadShutdownFinishTask> create() - { - return adoptPtr(new WorkerThreadShutdownFinishTask()); - } - - virtual void performTask(ScriptExecutionContext *context) - { - ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope()); - WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context); - // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. - workerGlobalScope->clearScript(); - } - - virtual bool isCleanupTask() const { return true; } -}; +void WorkerThread::stop() +{ + // Mutex protection is necessary because stop() can be called before the context is fully created. + LockHolder lock(m_threadCreationMutex); -class WorkerThreadShutdownStartTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<WorkerThreadShutdownStartTask> create() - { - return adoptPtr(new WorkerThreadShutdownStartTask()); - } + // 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. + if (m_workerGlobalScope) { + m_workerGlobalScope->script()->scheduleExecutionTermination(); - virtual void performTask(ScriptExecutionContext *context) - { - ASSERT_WITH_SECURITY_IMPLICATION(context->isWorkerGlobalScope()); - WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context); + m_runLoop.postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context ) { + WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context); -#if ENABLE(SQL_DATABASE) - // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below? - DatabaseTaskSynchronizer cleanupSync; - DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync); +#if ENABLE(INDEXED_DATABASE) + workerGlobalScope.stopIndexedDatabase(); #endif - workerGlobalScope->stopActiveDOMObjects(); + workerGlobalScope.stopActiveDOMObjects(); - workerGlobalScope->notifyObserversOfStop(); + workerGlobalScope.inspectorController().workerTerminating(); - // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, - // which become dangling once Heap is destroyed. - workerGlobalScope->removeAllEventListeners(); + // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, + // which become dangling once Heap is destroyed. + workerGlobalScope.removeAllEventListeners(); -#if ENABLE(SQL_DATABASE) - // We wait for the database thread to clean up all its stuff so that we - // can do more stringent leak checks as we exit. - cleanupSync.waitForTaskCompletion(); -#endif + // Stick a shutdown command at the end of the queue, so that we deal + // with all the cleanup tasks the databases post first. + workerGlobalScope.postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) { + WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context); + // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. + workerGlobalScope.clearScript(); + } }); - // Stick a shutdown command at the end of the queue, so that we deal - // with all the cleanup tasks the databases post first. - workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create()); + } }); + return; } + m_runLoop.terminate(); +} - virtual bool isCleanupTask() const { return true; } -}; - -void WorkerThread::stop() +void WorkerThread::releaseFastMallocFreeMemoryInAllThreads() { - // Mutex protection is necessary because stop() can be called before the context is fully created. - MutexLocker lock(m_threadCreationMutex); + std::lock_guard<StaticLock> lock(threadSetMutex); - // 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. - if (m_workerGlobalScope) { - m_workerGlobalScope->script()->scheduleExecutionTermination(); - -#if ENABLE(SQL_DATABASE) - DatabaseManager::manager().interruptAllDatabasesForContext(m_workerGlobalScope.get()); -#endif - m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create()); - return; + for (auto* workerThread : workerThreads()) { + workerThread->runLoop().postTask([] (ScriptExecutionContext&) { + WTF::releaseFastMallocFreeMemory(); + }); } - m_runLoop.terminate(); } -class ReleaseFastMallocFreeMemoryTask : public ScriptExecutionContext::Task { - virtual void performTask(ScriptExecutionContext*) override { WTF::releaseFastMallocFreeMemory(); } -}; - -void WorkerThread::releaseFastMallocFreeMemoryInAllThreads() +IDBClient::IDBConnectionProxy* WorkerThread::idbConnectionProxy() { - std::lock_guard<std::mutex> lock(threadSetMutex()); +#if ENABLE(INDEXED_DATABASE) + return m_idbConnectionProxy.get(); +#else + return nullptr; +#endif +} - for (auto* workerThread : workerThreads()) - workerThread->runLoop().postTask(adoptPtr(new ReleaseFastMallocFreeMemoryTask)); +SocketProvider* WorkerThread::socketProvider() +{ +#if ENABLE(WEB_SOCKETS) + return m_socketProvider.get(); +#else + return nullptr; +#endif } } // namespace WebCore |