#include "config.h"
#include "platform/scheduler/Scheduler.h"
-#include "platform/PlatformThreadData.h"
-#include "platform/Task.h"
-#include "platform/ThreadTimers.h"
-#include "platform/TraceEvent.h"
+#include "platform/TraceLocation.h"
#include "public/platform/Platform.h"
-#include "wtf/MainThread.h"
+#include "public/platform/WebScheduler.h"
+#include "public/platform/WebTraceLocation.h"
namespace blink {
-namespace {
-
-// The time we should stay in CompositorPriority mode for, after a touch event.
-double kLowSchedulerPolicyAfterTouchTimeSeconds = 0.1;
-
-// Can be created from any thread.
-// Note if the scheduler gets shutdown, this may be run after.
-class MainThreadIdleTaskAdapter : public WebThread::Task {
+class IdleTaskRunner : public WebScheduler::IdleTask {
public:
- MainThreadIdleTaskAdapter(const Scheduler::IdleTask& idleTask, double allottedTimeMs, const TraceLocation& location)
- : m_idleTask(idleTask)
- , m_allottedTimeMs(allottedTimeMs)
- , m_location(location)
+ explicit IdleTaskRunner(const Scheduler::IdleTask& task)
+ : m_task(task)
{
}
- // WebThread::Task implementation.
- virtual void run() OVERRIDE
- {
- TRACE_EVENT2("blink", "MainThreadIdleTaskAdapter::run",
- "src_file", m_location.fileName(),
- "src_func", m_location.functionName());
- m_idleTask(m_allottedTimeMs);
- }
-
-private:
- Scheduler::IdleTask m_idleTask;
- double m_allottedTimeMs;
- TraceLocation m_location;
-};
-
-} // namespace
-
-// Typically only created from compositor or render threads.
-// Note if the scheduler gets shutdown, this may be run after.
-class Scheduler::MainThreadPendingHighPriorityTaskRunner : public WebThread::Task {
-public:
- MainThreadPendingHighPriorityTaskRunner()
+ virtual ~IdleTaskRunner()
{
- ASSERT(Scheduler::shared());
}
- // WebThread::Task implementation.
- virtual void run() OVERRIDE
+ // WebScheduler::IdleTask implementation.
+ void run(double deadlineSeconds) override
{
- Scheduler* scheduler = Scheduler::shared();
- // FIXME: This check should't be necessary, tasks should not outlive blink.
- ASSERT(scheduler);
- if (!scheduler)
- return;
- // NOTE we must unconditionally execute high priority tasks here, since if we're not in CompositorPriority
- // mode, then this is the only place where high priority tasks will be executed.
- scheduler->swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting();
+ m_task(deadlineSeconds);
}
-};
-
-
-// Can be created from any thread.
-// Note if the scheduler gets shutdown, this may be run after.
-class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task {
-public:
- MainThreadPendingTaskRunner(
- const Scheduler::Task& task, const TraceLocation& location, const char* traceName)
- : m_task(task, location, traceName)
- {
- ASSERT(Scheduler::shared());
- }
-
- // WebThread::Task implementation.
- virtual void run() OVERRIDE
- {
- Scheduler* scheduler = Scheduler::shared();
- // FIXME: This check should't be necessary, tasks should not outlive blink.
- ASSERT(scheduler);
- if (scheduler)
- Scheduler::shared()->runPendingHighPriorityTasksIfInCompositorPriority();
- m_task.run();
- }
-
- TracedTask m_task;
+private:
+ Scheduler::IdleTask m_task;
};
Scheduler* Scheduler::s_sharedScheduler = nullptr;
-void Scheduler::initializeOnMainThread()
-{
- s_sharedScheduler = new Scheduler();
-}
-
void Scheduler::shutdown()
{
delete s_sharedScheduler;
Scheduler* Scheduler::shared()
{
+ if (!s_sharedScheduler)
+ s_sharedScheduler = new Scheduler(Platform::current()->scheduler());
return s_sharedScheduler;
}
-Scheduler::Scheduler()
- : m_sharedTimerFunction(nullptr)
- , m_mainThread(blink::Platform::current()->currentThread())
- , m_compositorPriorityPolicyEndTimeSeconds(0)
- , m_highPriorityTaskCount(0)
- , m_highPriorityTaskRunnerPosted(false)
- , m_schedulerPolicy(Normal)
+Scheduler::Scheduler(WebScheduler* webScheduler)
+ : m_webScheduler(webScheduler)
{
}
Scheduler::~Scheduler()
{
- while (hasPendingHighPriorityWork()) {
- swapQueuesAndRunPendingTasks();
- }
-}
-
-void Scheduler::willBeginFrame(const WebBeginFrameArgs& args)
-{
- // TODO: Use frame deadline and interval to schedule idle tasks.
-}
-
-void Scheduler::didCommitFrameToCompositor()
-{
- // TODO: Trigger the frame deadline immediately.
-}
-
-void Scheduler::scheduleIdleTask(const TraceLocation& location, const IdleTask& idleTask)
-{
- // TODO: send a real allottedTime here.
- m_mainThread->postTask(new MainThreadIdleTaskAdapter(idleTask, 0, location));
-}
-
-void Scheduler::postHighPriorityTaskInternal(const TraceLocation& location, const Task& task, const char* traceName)
-{
- Locker<Mutex> lock(m_pendingTasksMutex);
-
- m_pendingHighPriorityTasks.append(TracedTask(task, location, traceName));
- atomicIncrement(&m_highPriorityTaskCount);
- maybePostMainThreadPendingHighPriorityTaskRunner();
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPriorityTasks", m_highPriorityTaskCount);
-}
-
-void Scheduler::postTask(const TraceLocation& location, const Task& task)
-{
- m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Scheduler::MainThreadTask"));
-}
-
-void Scheduler::postInputTask(const TraceLocation& location, const Task& task)
-{
- postHighPriorityTaskInternal(location, task, "Scheduler::InputTask");
-}
-
-void Scheduler::didReceiveInputEvent()
-{
- enterSchedulerPolicy(CompositorPriority);
-}
-
-void Scheduler::postCompositorTask(const TraceLocation& location, const Task& task)
-{
- postHighPriorityTaskInternal(location, task, "Scheduler::CompositorTask");
-}
-
-void Scheduler::postIpcTask(const TraceLocation& location, const Task& task)
-{
- // FIXME: we want IPCs to be high priority, but we can't currently do that because some of them can take a very long
- // time to process. These need refactoring but we need to add some infrastructure to identify them.
- m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Scheduler::IpcTask"));
-}
-
-void Scheduler::maybePostMainThreadPendingHighPriorityTaskRunner()
-{
- ASSERT(m_pendingTasksMutex.locked());
- if (m_highPriorityTaskRunnerPosted)
- return;
- m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner());
- m_highPriorityTaskRunnerPosted = true;
+ if (m_webScheduler)
+ m_webScheduler->shutdown();
}
void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idleTask)
{
- scheduleIdleTask(location, idleTask);
-}
-
-void Scheduler::tickSharedTimer()
-{
- TRACE_EVENT0("blink", "Scheduler::tickSharedTimer");
-
- // Run any high priority tasks that are queued up, otherwise the blink timers will yield immediately.
- bool workDone = runPendingHighPriorityTasksIfInCompositorPriority();
- m_sharedTimerFunction();
-
- // The blink timers may have just yielded, so run any high priority tasks that where queued up
- // while the blink timers were executing.
- if (!workDone)
- runPendingHighPriorityTasksIfInCompositorPriority();
-}
-
-bool Scheduler::runPendingHighPriorityTasksIfInCompositorPriority()
-{
- ASSERT(isMainThread());
- if (schedulerPolicy() != CompositorPriority)
- return false;
-
- return swapQueuesAndRunPendingTasks();
-}
-
-bool Scheduler::swapQueuesAndRunPendingTasks()
-{
- ASSERT(isMainThread());
-
- // These locks guard against another thread posting input or compositor tasks while we swap the buffers.
- // One the buffers have been swapped we can safely access the returned deque without having to lock.
- m_pendingTasksMutex.lock();
- Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffers();
- maybeEnterNormalSchedulerPolicy();
- m_pendingTasksMutex.unlock();
- return executeHighPriorityTasks(highPriorityTasks);
-}
-
-void Scheduler::swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting()
-{
- ASSERT(isMainThread());
-
- // These locks guard against another thread posting input or compositor tasks while we swap the buffers.
- // One the buffers have been swapped we can safely access the returned deque without having to lock.
- m_pendingTasksMutex.lock();
- Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffers();
- m_highPriorityTaskRunnerPosted = false;
- maybeEnterNormalSchedulerPolicy();
- m_pendingTasksMutex.unlock();
- executeHighPriorityTasks(highPriorityTasks);
-}
-
-void Scheduler::maybeEnterNormalSchedulerPolicy()
-{
- ASSERT(isMainThread());
- ASSERT(m_pendingTasksMutex.locked());
-
- // Go back to the normal scheduler policy if enough time has elapsed.
- if (schedulerPolicy() == CompositorPriority && Platform::current()->monotonicallyIncreasingTime() > m_compositorPriorityPolicyEndTimeSeconds)
- enterSchedulerPolicyLocked(Normal);
-}
-
-bool Scheduler::executeHighPriorityTasks(Deque<TracedTask>& highPriorityTasks)
-{
- TRACE_EVENT0("blink", "Scheduler::executeHighPriorityTasks");
- int highPriorityTasksExecuted = 0;
- while (!highPriorityTasks.isEmpty()) {
- highPriorityTasks.takeFirst().run();
- highPriorityTasksExecuted++;
- }
-
- int highPriorityTaskCount = atomicSubtract(&m_highPriorityTaskCount, highPriorityTasksExecuted);
- ASSERT_UNUSED(highPriorityTaskCount, highPriorityTaskCount >= 0);
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPriorityTasks", m_highPriorityTaskCount);
- return highPriorityTasksExecuted > 0;
-}
-
-void Scheduler::sharedTimerAdapter()
-{
- shared()->tickSharedTimer();
-}
-
-void Scheduler::setSharedTimerFiredFunction(void (*function)())
-{
- m_sharedTimerFunction = function;
- blink::Platform::current()->setSharedTimerFiredFunction(function ? &Scheduler::sharedTimerAdapter : nullptr);
-}
-
-void Scheduler::setSharedTimerFireInterval(double interval)
-{
- blink::Platform::current()->setSharedTimerFireInterval(interval);
-}
-
-void Scheduler::stopSharedTimer()
-{
- blink::Platform::current()->stopSharedTimer();
+ if (m_webScheduler)
+ m_webScheduler->postIdleTask(WebTraceLocation(location), new IdleTaskRunner(idleTask));
}
bool Scheduler::shouldYieldForHighPriorityWork() const
{
- // It's only worthwhile yielding in CompositorPriority mode.
- if (schedulerPolicy() != CompositorPriority)
- return false;
-
- return hasPendingHighPriorityWork();
-}
-
-bool Scheduler::hasPendingHighPriorityWork() const
-{
- // This method is expected to be run on the main thread, but the high priority tasks will be posted by
- // other threads. We could use locks here, but this function is (sometimes) called a lot by
- // ThreadTimers::sharedTimerFiredInternal so we decided to use atomics + barrier loads here which
- // should be cheaper.
- // NOTE it's possible the barrier read is overkill here, since delayed yielding isn't a big deal.
- return acquireLoad(&m_highPriorityTaskCount) != 0;
-}
-
-Scheduler::SchedulerPolicy Scheduler::schedulerPolicy() const
-{
- ASSERT(isMainThread());
- // It's important not to miss the transition from normal to low latency mode, otherwise we're likely to
- // delay the processing of input tasks. Since that transition is triggered by a different thread, we
- // need either a lock or a memory barrier, and the memory barrier is probably cheaper.
- return static_cast<SchedulerPolicy>(acquireLoad(&m_schedulerPolicy));
-}
-
-void Scheduler::enterSchedulerPolicy(SchedulerPolicy schedulerPolicy)
-{
- Locker<Mutex> lock(m_pendingTasksMutex);
- enterSchedulerPolicyLocked(schedulerPolicy);
-}
-
-void Scheduler::enterSchedulerPolicyLocked(SchedulerPolicy schedulerPolicy)
-{
- ASSERT(m_pendingTasksMutex.locked());
- if (schedulerPolicy == CompositorPriority)
- m_compositorPriorityPolicyEndTimeSeconds = Platform::current()->monotonicallyIncreasingTime() + kLowSchedulerPolicyAfterTouchTimeSeconds;
-
- releaseStore(&m_schedulerPolicy, schedulerPolicy);
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "SchedulerPolicy", schedulerPolicy);
+ if (m_webScheduler)
+ return m_webScheduler->shouldYieldForHighPriorityWork();
+ return false;
}
} // namespace blink