#include "config.h"
#include "WorkerThread.h"
#include "DedicatedWorkerGlobalScope.h"
#include "InspectorInstrumentation.h"
#include "ScriptSourceCode.h"
#include "SecurityOrigin.h"
#include "ThreadGlobalData.h"
#include "URL.h"
#include <utility>
#include <wtf/NeverDestroyed.h>
#include <wtf/Noncopyable.h>
#include <wtf/text/WTFString.h>
#if PLATFORM(IOS)
#include "FloatingPointEnvironment.h"
#include "WebCoreThread.h"
#endif
namespace WebCore {
static std::mutex& threadSetMutex()
{
static std::once_flag onceFlag;
static LazyNeverDestroyed<std::mutex> mutex;
std::call_once(onceFlag, []{
mutex.construct();
});
return mutex;
}
static HashSet<WorkerThread*>& workerThreads()
{
static NeverDestroyed<HashSet<WorkerThread*>> workerThreads;
return workerThreads;
}
unsigned WorkerThread::workerThreadCount()
{
std::lock_guard<std::mutex> lock(threadSetMutex());
return workerThreads().size();
}
struct WorkerThreadStartupData {
WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED;
public:
WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin);
URL m_scriptURL;
String m_userAgent;
String m_sourceCode;
WorkerThreadStartMode m_startMode;
String m_contentSecurityPolicy;
ContentSecurityPolicy::HeaderType m_contentSecurityPolicyType;
RefPtr<SecurityOrigin> m_topOrigin;
};
WorkerThreadStartupData::WorkerThreadStartupData(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
: m_scriptURL(scriptURL.isolatedCopy())
, m_userAgent(userAgent.isolatedCopy())
, m_sourceCode(sourceCode.isolatedCopy())
, m_startMode(startMode)
, m_contentSecurityPolicy(contentSecurityPolicy.isolatedCopy())
, m_contentSecurityPolicyType(contentSecurityPolicyType)
, m_topOrigin(topOrigin ? &topOrigin->isolatedCopy().get() : nullptr)
{
}
WorkerThread::WorkerThread(const URL& scriptURL, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType, const SecurityOrigin* topOrigin)
: m_threadID(0)
, m_workerLoaderProxy(workerLoaderProxy)
, m_workerReportingProxy(workerReportingProxy)
, m_startupData(std::make_unique<WorkerThreadStartupData>(scriptURL, userAgent, sourceCode, startMode, contentSecurityPolicy, contentSecurityPolicyType, topOrigin))
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
, m_notificationClient(0)
#endif
{
std::lock_guard<std::mutex> lock(threadSetMutex());
workerThreads().add(this);
}
WorkerThread::~WorkerThread()
{
std::lock_guard<std::mutex> lock(threadSetMutex());
ASSERT(workerThreads().contains(this));
workerThreads().remove(this);
}
bool WorkerThread::start()
{
MutexLocker lock(m_threadCreationMutex);
if (m_threadID)
return true;
m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker");
return m_threadID;
}
void WorkerThread::workerThreadStart(void* thread)
{
static_cast<WorkerThread*>(thread)->workerThread();
}
void WorkerThread::workerThread()
{
#if PLATFORM(IOS)
FloatingPointEnvironment::singleton().propagateMainThreadEnvironment();
#endif
{
MutexLocker lock(m_threadCreationMutex);
m_workerGlobalScope = createWorkerGlobalScope(m_startupData->m_scriptURL, m_startupData->m_userAgent, m_startupData->m_contentSecurityPolicy, m_startupData->m_contentSecurityPolicyType, m_startupData->m_topOrigin.release());
if (m_runLoop.terminated()) {
m_workerGlobalScope->script()->forbidExecution();
}
}
WorkerScriptController* script = m_workerGlobalScope->script();
InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), m_startupData->m_startMode);
script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL));
m_startupData = nullptr;
runEventLoop();
ThreadIdentifier threadID = m_threadID;
ASSERT(m_workerGlobalScope->hasOneRef());
m_workerGlobalScope = nullptr;
threadGlobalData().destroy();
detachThread(threadID);
}
void WorkerThread::runEventLoop()
{
m_runLoop.run(m_workerGlobalScope.get());
}
void WorkerThread::stop()
{
MutexLocker lock(m_threadCreationMutex);
if (m_workerGlobalScope) {
m_workerGlobalScope->script()->scheduleExecutionTermination();
m_runLoop.postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context ) {
WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
workerGlobalScope.stopActiveDOMObjects();
workerGlobalScope.notifyObserversOfStop();
workerGlobalScope.removeAllEventListeners();
workerGlobalScope.postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) {
WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
workerGlobalScope.clearScript();
} });
} });
return;
}
m_runLoop.terminate();
}
void WorkerThread::releaseFastMallocFreeMemoryInAllThreads()
{
std::lock_guard<std::mutex> lock(threadSetMutex());
for (auto* workerThread : workerThreads()) {
workerThread->runLoop().postTask([] (ScriptExecutionContext&) {
WTF::releaseFastMallocFreeMemory();
});
}
}
}