#include "config.h"
#include "ScriptExecutionContext.h"
#include "SharedTimer.h"
#include "ThreadGlobalData.h"
#include "ThreadTimers.h"
#include "WorkerRunLoop.h"
#include "WorkerGlobalScope.h"
#include "WorkerThread.h"
#include <wtf/CurrentTime.h>
#if PLATFORM(GTK)
#include <glib.h>
#endif
namespace WebCore {
class WorkerSharedTimer final : public SharedTimer {
public:
void setFiredFunction(std::function<void()>&& function) override { m_sharedTimerFunction = WTFMove(function); }
void setFireInterval(double interval) override { m_nextFireTime = interval + currentTime(); }
void stop() override { m_nextFireTime = 0; }
bool isActive() { return m_sharedTimerFunction && m_nextFireTime; }
double fireTime() { return m_nextFireTime; }
void fire() { m_sharedTimerFunction(); }
private:
std::function<void()> m_sharedTimerFunction;
double m_nextFireTime { 0 };
};
class ModePredicate {
public:
ModePredicate(const String& mode)
: m_mode(mode)
, m_defaultMode(mode == WorkerRunLoop::defaultMode())
{
}
bool isDefaultMode() const
{
return m_defaultMode;
}
bool operator()(const WorkerRunLoop::Task& task) const
{
return m_defaultMode || m_mode == task.mode();
}
private:
String m_mode;
bool m_defaultMode;
};
WorkerRunLoop::WorkerRunLoop()
: m_sharedTimer(std::make_unique<WorkerSharedTimer>())
, m_nestedCount(0)
, m_uniqueId(0)
{
}
WorkerRunLoop::~WorkerRunLoop()
{
ASSERT(!m_nestedCount);
}
String WorkerRunLoop::defaultMode()
{
return String();
}
class RunLoopSetup {
WTF_MAKE_NONCOPYABLE(RunLoopSetup);
public:
RunLoopSetup(WorkerRunLoop& runLoop)
: m_runLoop(runLoop)
{
if (!m_runLoop.m_nestedCount)
threadGlobalData().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get());
m_runLoop.m_nestedCount++;
}
~RunLoopSetup()
{
m_runLoop.m_nestedCount--;
if (!m_runLoop.m_nestedCount)
threadGlobalData().threadTimers().setSharedTimer(0);
}
private:
WorkerRunLoop& m_runLoop;
};
void WorkerRunLoop::run(WorkerGlobalScope* context)
{
RunLoopSetup setup(*this);
ModePredicate modePredicate(defaultMode());
MessageQueueWaitResult result;
do {
result = runInMode(context, modePredicate, WaitForMessage);
} while (result != MessageQueueTerminated);
runCleanupTasks(context);
}
MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const String& mode, WaitMode waitMode)
{
RunLoopSetup setup(*this);
ModePredicate modePredicate(mode);
MessageQueueWaitResult result = runInMode(context, modePredicate, waitMode);
return result;
}
MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const ModePredicate& predicate, WaitMode waitMode)
{
ASSERT(context);
ASSERT(context->thread().threadID() == currentThread());
#if PLATFORM(GTK)
GMainContext* mainContext = g_main_context_get_thread_default();
if (g_main_context_pending(mainContext))
g_main_context_iteration(mainContext, FALSE);
#endif
double deadline = MessageQueue<Task>::infiniteTime();
#if USE(CF)
CFAbsoluteTime nextCFRunLoopTimerFireDate = CFRunLoopGetNextTimerFireDate(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
double timeUntilNextCFRunLoopTimerInSeconds = nextCFRunLoopTimerFireDate - CFAbsoluteTimeGetCurrent();
deadline = currentTime() + std::max(0.0, timeUntilNextCFRunLoopTimerInSeconds);
#endif
double absoluteTime = 0.0;
if (waitMode == WaitForMessage) {
if (predicate.isDefaultMode() && m_sharedTimer->isActive())
absoluteTime = std::min(deadline, m_sharedTimer->fireTime());
else
absoluteTime = deadline;
}
MessageQueueWaitResult result;
auto task = m_messageQueue.waitForMessageFilteredWithTimeout(result, predicate, absoluteTime);
switch (result) {
case MessageQueueTerminated:
break;
case MessageQueueMessageReceived:
task->performTask(*this, context);
break;
case MessageQueueTimeout:
if (!context->isClosing())
m_sharedTimer->fire();
#if USE(CF)
if (nextCFRunLoopTimerFireDate <= CFAbsoluteTimeGetCurrent())
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
#endif
break;
}
return result;
}
void WorkerRunLoop::runCleanupTasks(WorkerGlobalScope* context)
{
ASSERT(context);
ASSERT(context->thread().threadID() == currentThread());
ASSERT(m_messageQueue.killed());
while (true) {
auto task = m_messageQueue.tryGetMessageIgnoringKilled();
if (!task)
return;
task->performTask(*this, context);
}
}
void WorkerRunLoop::terminate()
{
m_messageQueue.kill();
}
void WorkerRunLoop::postTask(ScriptExecutionContext::Task&& task)
{
postTaskForMode(WTFMove(task), defaultMode());
}
void WorkerRunLoop::postTaskAndTerminate(ScriptExecutionContext::Task&& task)
{
m_messageQueue.appendAndKill(std::make_unique<Task>(WTFMove(task), defaultMode()));
}
void WorkerRunLoop::postTaskForMode(ScriptExecutionContext::Task&& task, const String& mode)
{
m_messageQueue.append(std::make_unique<Task>(WTFMove(task), mode));
}
void WorkerRunLoop::Task::performTask(const WorkerRunLoop& runLoop, WorkerGlobalScope* context)
{
if ((!context->isClosing() && !runLoop.terminated()) || m_task.isCleanupTask())
m_task.performTask(*context);
}
WorkerRunLoop::Task::Task(ScriptExecutionContext::Task&& task, const String& mode)
: m_task(WTFMove(task))
, m_mode(mode.isolatedCopy())
{
}
}