WorkerOrWorkletThread.cpp [plain text]
#include "config.h"
#include "WorkerOrWorkletThread.h"
#include "ThreadGlobalData.h"
#include "WorkerOrWorkletGlobalScope.h"
#include "WorkerOrWorkletScriptController.h"
#if PLATFORM(IOS_FAMILY)
#include "FloatingPointEnvironment.h"
#endif
#if USE(GLIB)
#include <wtf/glib/GRefPtr.h>
#endif
namespace WebCore {
Lock& WorkerOrWorkletThread::workerOrWorkletThreadsLock()
{
static Lock mutex;
return mutex;
}
HashSet<WorkerOrWorkletThread*>& WorkerOrWorkletThread::workerOrWorkletThreads()
{
ASSERT(workerOrWorkletThreadsLock().isHeld());
static NeverDestroyed<HashSet<WorkerOrWorkletThread*>> workerOrWorkletThreads;
return workerOrWorkletThreads;
}
WorkerOrWorkletThread::WorkerOrWorkletThread(const String& identifier)
: m_identifier(identifier)
{
auto locker = holdLock(workerOrWorkletThreadsLock());
workerOrWorkletThreads().add(this);
}
WorkerOrWorkletThread::~WorkerOrWorkletThread()
{
auto locker = holdLock(workerOrWorkletThreadsLock());
ASSERT(workerOrWorkletThreads().contains(this));
workerOrWorkletThreads().remove(this);
}
void WorkerOrWorkletThread::startRunningDebuggerTasks()
{
ASSERT(!m_pausedForDebugger);
m_pausedForDebugger = true;
MessageQueueWaitResult result;
do {
result = m_runLoop.runInDebuggerMode(*m_globalScope);
} while (result != MessageQueueTerminated && m_pausedForDebugger);
}
void WorkerOrWorkletThread::stopRunningDebuggerTasks()
{
m_pausedForDebugger = false;
}
void WorkerOrWorkletThread::runEventLoop()
{
m_runLoop.run(m_globalScope.get());
}
void WorkerOrWorkletThread::workerOrWorkletThread()
{
auto protectedThis = makeRef(*this);
#if PLATFORM(IOS_FAMILY)
FloatingPointEnvironment::singleton().propagateMainThreadEnvironment();
#endif
#if USE(GLIB)
GRefPtr<GMainContext> mainContext = adoptGRef(g_main_context_new());
g_main_context_push_thread_default(mainContext.get());
#endif
WorkerOrWorkletScriptController* scriptController;
{
auto locker = holdLock(m_threadCreationAndGlobalScopeLock);
m_globalScope = createGlobalScope();
scriptController = m_globalScope->script();
if (m_runLoop.terminated()) {
scriptController->scheduleExecutionTermination();
scriptController->forbidExecution();
}
}
if (shouldWaitForWebInspectorOnStartup()) {
startRunningDebuggerTasks();
if (m_runLoop.terminated())
scriptController->forbidExecution();
}
String exceptionMessage;
evaluateScriptIfNecessary(exceptionMessage);
callOnMainThread([evaluateCallback = WTFMove(m_evaluateCallback), message = exceptionMessage.isolatedCopy()] {
if (evaluateCallback)
evaluateCallback(message);
});
runEventLoop();
#if USE(GLIB)
g_main_context_pop_thread_default(mainContext.get());
#endif
RefPtr<Thread> protector = m_thread;
ASSERT(m_globalScope->hasOneRef());
RefPtr<WorkerOrWorkletGlobalScope> workerGlobalScopeToDelete;
{
auto locker = holdLock(m_threadCreationAndGlobalScopeLock);
workerGlobalScopeToDelete = std::exchange(m_globalScope, nullptr);
if (m_stoppedCallback)
callOnMainThread(WTFMove(m_stoppedCallback));
}
workerGlobalScopeToDelete = nullptr;
threadGlobalData().destroy();
callOnMainThread([protectedThis = WTFMove(protectedThis)] { });
protector->detach();
}
void WorkerOrWorkletThread::start(WTF::Function<void(const String&)>&& evaluateCallback)
{
auto locker = holdLock(m_threadCreationAndGlobalScopeLock);
if (m_thread)
return;
m_evaluateCallback = WTFMove(evaluateCallback);
auto thread = createThread();
WTF::storeStoreFence();
m_thread = WTFMove(thread);
}
void WorkerOrWorkletThread::stop(Function<void()>&& stoppedCallback)
{
auto locker = tryHoldLock(m_threadCreationAndGlobalScopeLock);
if (!locker) {
callOnMainThread([this, stoppedCallback = WTFMove(stoppedCallback)]() mutable {
stop(WTFMove(stoppedCallback));
});
return;
}
if (m_isSuspended)
resume();
ASSERT(!m_stoppedCallback);
m_stoppedCallback = WTFMove(stoppedCallback);
if (globalScope()) {
globalScope()->script()->scheduleExecutionTermination();
m_runLoop.postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context ) {
auto& globalScope = downcast<WorkerOrWorkletGlobalScope>(context);
globalScope.prepareForDestruction();
globalScope.postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) {
auto& globalScope = downcast<WorkerOrWorkletGlobalScope>(context);
globalScope.clearScript();
} });
} });
return;
}
m_runLoop.terminate();
}
void WorkerOrWorkletThread::suspend()
{
m_isSuspended = true;
m_runLoop.postTask([&](ScriptExecutionContext&) {
if (globalScope())
globalScope()->suspend();
m_suspensionSemaphore.wait();
if (globalScope())
globalScope()->resume();
});
}
void WorkerOrWorkletThread::resume()
{
ASSERT(m_isSuspended);
m_isSuspended = false;
m_suspensionSemaphore.signal();
}
void WorkerOrWorkletThread::releaseFastMallocFreeMemoryInAllThreads()
{
auto locker = holdLock(workerOrWorkletThreadsLock());
for (auto* workerOrWorkletThread : workerOrWorkletThreads()) {
workerOrWorkletThread->runLoop().postTask([] (ScriptExecutionContext&) {
WTF::releaseFastMallocFreeMemory();
});
}
}
}