Home | History | Annotate | Download | only in scheduler
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "platform/scheduler/Scheduler.h"
      7 
      8 #include "platform/PlatformThreadData.h"
      9 #include "platform/Task.h"
     10 #include "platform/ThreadTimers.h"
     11 #include "platform/TraceEvent.h"
     12 #include "public/platform/Platform.h"
     13 #include "wtf/MainThread.h"
     14 
     15 namespace blink {
     16 
     17 namespace {
     18 
     19 // The time we should stay in CompositorPriority mode for, after a touch event.
     20 double kLowSchedulerPolicyAfterTouchTimeSeconds = 0.1;
     21 
     22 // Can be created from any thread.
     23 // Note if the scheduler gets shutdown, this may be run after.
     24 class MainThreadIdleTaskAdapter : public WebThread::Task {
     25 public:
     26     MainThreadIdleTaskAdapter(const Scheduler::IdleTask& idleTask, double allottedTimeMs, const TraceLocation& location)
     27         : m_idleTask(idleTask)
     28         , m_allottedTimeMs(allottedTimeMs)
     29         , m_location(location)
     30     {
     31     }
     32 
     33     // WebThread::Task implementation.
     34     virtual void run() OVERRIDE
     35     {
     36         TRACE_EVENT2("blink", "MainThreadIdleTaskAdapter::run",
     37             "src_file", m_location.fileName(),
     38             "src_func", m_location.functionName());
     39         m_idleTask(m_allottedTimeMs);
     40     }
     41 
     42 private:
     43     Scheduler::IdleTask m_idleTask;
     44     double m_allottedTimeMs;
     45     TraceLocation m_location;
     46 };
     47 
     48 } // namespace
     49 
     50 // Typically only created from compositor or render threads.
     51 // Note if the scheduler gets shutdown, this may be run after.
     52 class Scheduler::MainThreadPendingHighPriorityTaskRunner : public WebThread::Task {
     53 public:
     54     MainThreadPendingHighPriorityTaskRunner()
     55     {
     56         ASSERT(Scheduler::shared());
     57     }
     58 
     59     // WebThread::Task implementation.
     60     virtual void run() OVERRIDE
     61     {
     62         Scheduler* scheduler = Scheduler::shared();
     63         // FIXME: This check should't be necessary, tasks should not outlive blink.
     64         ASSERT(scheduler);
     65         if (!scheduler)
     66             return;
     67         // NOTE we must unconditionally execute high priority tasks here, since if we're not in CompositorPriority
     68         // mode, then this is the only place where high priority tasks will be executed.
     69         scheduler->swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting();
     70     }
     71 };
     72 
     73 
     74 // Can be created from any thread.
     75 // Note if the scheduler gets shutdown, this may be run after.
     76 class Scheduler::MainThreadPendingTaskRunner : public WebThread::Task {
     77 public:
     78     MainThreadPendingTaskRunner(
     79         const Scheduler::Task& task, const TraceLocation& location, const char* traceName)
     80         : m_task(task, location, traceName)
     81     {
     82         ASSERT(Scheduler::shared());
     83     }
     84 
     85     // WebThread::Task implementation.
     86     virtual void run() OVERRIDE
     87     {
     88         Scheduler* scheduler = Scheduler::shared();
     89         // FIXME: This check should't be necessary, tasks should not outlive blink.
     90         ASSERT(scheduler);
     91         if (scheduler)
     92             Scheduler::shared()->runPendingHighPriorityTasksIfInCompositorPriority();
     93         m_task.run();
     94     }
     95 
     96     TracedTask m_task;
     97 };
     98 
     99 Scheduler* Scheduler::s_sharedScheduler = nullptr;
    100 
    101 void Scheduler::initializeOnMainThread()
    102 {
    103     s_sharedScheduler = new Scheduler();
    104 }
    105 
    106 void Scheduler::shutdown()
    107 {
    108     delete s_sharedScheduler;
    109     s_sharedScheduler = nullptr;
    110 }
    111 
    112 Scheduler* Scheduler::shared()
    113 {
    114     return s_sharedScheduler;
    115 }
    116 
    117 Scheduler::Scheduler()
    118     : m_sharedTimerFunction(nullptr)
    119     , m_mainThread(blink::Platform::current()->currentThread())
    120     , m_compositorPriorityPolicyEndTimeSeconds(0)
    121     , m_highPriorityTaskCount(0)
    122     , m_highPriorityTaskRunnerPosted(false)
    123     , m_schedulerPolicy(Normal)
    124 {
    125 }
    126 
    127 Scheduler::~Scheduler()
    128 {
    129     while (hasPendingHighPriorityWork()) {
    130         swapQueuesAndRunPendingTasks();
    131     }
    132 }
    133 
    134 void Scheduler::willBeginFrame(const WebBeginFrameArgs& args)
    135 {
    136     // TODO: Use frame deadline and interval to schedule idle tasks.
    137 }
    138 
    139 void Scheduler::didCommitFrameToCompositor()
    140 {
    141     // TODO: Trigger the frame deadline immediately.
    142 }
    143 
    144 void Scheduler::scheduleIdleTask(const TraceLocation& location, const IdleTask& idleTask)
    145 {
    146     // TODO: send a real allottedTime here.
    147     m_mainThread->postTask(new MainThreadIdleTaskAdapter(idleTask, 0, location));
    148 }
    149 
    150 void Scheduler::postHighPriorityTaskInternal(const TraceLocation& location, const Task& task, const char* traceName)
    151 {
    152     Locker<Mutex> lock(m_pendingTasksMutex);
    153 
    154     m_pendingHighPriorityTasks.append(TracedTask(task, location, traceName));
    155     atomicIncrement(&m_highPriorityTaskCount);
    156     maybePostMainThreadPendingHighPriorityTaskRunner();
    157     TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPriorityTasks", m_highPriorityTaskCount);
    158 }
    159 
    160 void Scheduler::postTask(const TraceLocation& location, const Task& task)
    161 {
    162     m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Scheduler::MainThreadTask"));
    163 }
    164 
    165 void Scheduler::postInputTask(const TraceLocation& location, const Task& task)
    166 {
    167     postHighPriorityTaskInternal(location, task, "Scheduler::InputTask");
    168 }
    169 
    170 void Scheduler::didReceiveInputEvent()
    171 {
    172     enterSchedulerPolicy(CompositorPriority);
    173 }
    174 
    175 void Scheduler::postCompositorTask(const TraceLocation& location, const Task& task)
    176 {
    177     postHighPriorityTaskInternal(location, task, "Scheduler::CompositorTask");
    178 }
    179 
    180 void Scheduler::postIpcTask(const TraceLocation& location, const Task& task)
    181 {
    182     // FIXME: we want IPCs to be high priority, but we can't currently do that because some of them can take a very long
    183     // time to process. These need refactoring but we need to add some infrastructure to identify them.
    184     m_mainThread->postTask(new MainThreadPendingTaskRunner(task, location, "Scheduler::IpcTask"));
    185 }
    186 
    187 void Scheduler::maybePostMainThreadPendingHighPriorityTaskRunner()
    188 {
    189     ASSERT(m_pendingTasksMutex.locked());
    190     if (m_highPriorityTaskRunnerPosted)
    191         return;
    192     m_mainThread->postTask(new MainThreadPendingHighPriorityTaskRunner());
    193     m_highPriorityTaskRunnerPosted = true;
    194 }
    195 
    196 void Scheduler::postIdleTask(const TraceLocation& location, const IdleTask& idleTask)
    197 {
    198     scheduleIdleTask(location, idleTask);
    199 }
    200 
    201 void Scheduler::tickSharedTimer()
    202 {
    203     TRACE_EVENT0("blink", "Scheduler::tickSharedTimer");
    204 
    205     // Run any high priority tasks that are queued up, otherwise the blink timers will yield immediately.
    206     bool workDone = runPendingHighPriorityTasksIfInCompositorPriority();
    207     m_sharedTimerFunction();
    208 
    209     // The blink timers may have just yielded, so run any high priority tasks that where queued up
    210     // while the blink timers were executing.
    211     if (!workDone)
    212         runPendingHighPriorityTasksIfInCompositorPriority();
    213 }
    214 
    215 bool Scheduler::runPendingHighPriorityTasksIfInCompositorPriority()
    216 {
    217     ASSERT(isMainThread());
    218     if (schedulerPolicy() != CompositorPriority)
    219         return false;
    220 
    221     return swapQueuesAndRunPendingTasks();
    222 }
    223 
    224 bool Scheduler::swapQueuesAndRunPendingTasks()
    225 {
    226     ASSERT(isMainThread());
    227 
    228     // These locks guard against another thread posting input or compositor tasks while we swap the buffers.
    229     // One the buffers have been swapped we can safely access the returned deque without having to lock.
    230     m_pendingTasksMutex.lock();
    231     Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffers();
    232     maybeEnterNormalSchedulerPolicy();
    233     m_pendingTasksMutex.unlock();
    234     return executeHighPriorityTasks(highPriorityTasks);
    235 }
    236 
    237 void Scheduler::swapQueuesRunPendingTasksAndAllowHighPriorityTaskRunnerPosting()
    238 {
    239     ASSERT(isMainThread());
    240 
    241     // These locks guard against another thread posting input or compositor tasks while we swap the buffers.
    242     // One the buffers have been swapped we can safely access the returned deque without having to lock.
    243     m_pendingTasksMutex.lock();
    244     Deque<TracedTask>& highPriorityTasks = m_pendingHighPriorityTasks.swapBuffers();
    245     m_highPriorityTaskRunnerPosted = false;
    246     maybeEnterNormalSchedulerPolicy();
    247     m_pendingTasksMutex.unlock();
    248     executeHighPriorityTasks(highPriorityTasks);
    249 }
    250 
    251 void Scheduler::maybeEnterNormalSchedulerPolicy()
    252 {
    253     ASSERT(isMainThread());
    254     ASSERT(m_pendingTasksMutex.locked());
    255 
    256     // Go back to the normal scheduler policy if enough time has elapsed.
    257     if (schedulerPolicy() == CompositorPriority && Platform::current()->monotonicallyIncreasingTime() > m_compositorPriorityPolicyEndTimeSeconds)
    258         enterSchedulerPolicyLocked(Normal);
    259 }
    260 
    261 bool Scheduler::executeHighPriorityTasks(Deque<TracedTask>& highPriorityTasks)
    262 {
    263     TRACE_EVENT0("blink", "Scheduler::executeHighPriorityTasks");
    264     int highPriorityTasksExecuted = 0;
    265     while (!highPriorityTasks.isEmpty()) {
    266         highPriorityTasks.takeFirst().run();
    267         highPriorityTasksExecuted++;
    268     }
    269 
    270     int highPriorityTaskCount = atomicSubtract(&m_highPriorityTaskCount, highPriorityTasksExecuted);
    271     ASSERT_UNUSED(highPriorityTaskCount, highPriorityTaskCount >= 0);
    272     TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "PendingHighPriorityTasks", m_highPriorityTaskCount);
    273     return highPriorityTasksExecuted > 0;
    274 }
    275 
    276 void Scheduler::sharedTimerAdapter()
    277 {
    278     shared()->tickSharedTimer();
    279 }
    280 
    281 void Scheduler::setSharedTimerFiredFunction(void (*function)())
    282 {
    283     m_sharedTimerFunction = function;
    284     blink::Platform::current()->setSharedTimerFiredFunction(function ? &Scheduler::sharedTimerAdapter : nullptr);
    285 }
    286 
    287 void Scheduler::setSharedTimerFireInterval(double interval)
    288 {
    289     blink::Platform::current()->setSharedTimerFireInterval(interval);
    290 }
    291 
    292 void Scheduler::stopSharedTimer()
    293 {
    294     blink::Platform::current()->stopSharedTimer();
    295 }
    296 
    297 bool Scheduler::shouldYieldForHighPriorityWork() const
    298 {
    299     // It's only worthwhile yielding in CompositorPriority mode.
    300     if (schedulerPolicy() != CompositorPriority)
    301         return false;
    302 
    303     return hasPendingHighPriorityWork();
    304 }
    305 
    306 bool Scheduler::hasPendingHighPriorityWork() const
    307 {
    308     // This method is expected to be run on the main thread, but the high priority tasks will be posted by
    309     // other threads. We could use locks here, but this function is (sometimes) called a lot by
    310     // ThreadTimers::sharedTimerFiredInternal so we decided to use atomics + barrier loads here which
    311     // should be cheaper.
    312     // NOTE it's possible the barrier read is overkill here, since delayed yielding isn't a big deal.
    313     return acquireLoad(&m_highPriorityTaskCount) != 0;
    314 }
    315 
    316 Scheduler::SchedulerPolicy Scheduler::schedulerPolicy() const
    317 {
    318     ASSERT(isMainThread());
    319     // It's important not to miss the transition from normal to low latency mode, otherwise we're likely to
    320     // delay the processing of input tasks. Since that transition is triggered by a different thread, we
    321     // need either a lock or a memory barrier, and the memory barrier is probably cheaper.
    322     return static_cast<SchedulerPolicy>(acquireLoad(&m_schedulerPolicy));
    323 }
    324 
    325 void Scheduler::enterSchedulerPolicy(SchedulerPolicy schedulerPolicy)
    326 {
    327     Locker<Mutex> lock(m_pendingTasksMutex);
    328     enterSchedulerPolicyLocked(schedulerPolicy);
    329 }
    330 
    331 void Scheduler::enterSchedulerPolicyLocked(SchedulerPolicy schedulerPolicy)
    332 {
    333     ASSERT(m_pendingTasksMutex.locked());
    334     if (schedulerPolicy == CompositorPriority)
    335         m_compositorPriorityPolicyEndTimeSeconds = Platform::current()->monotonicallyIncreasingTime() + kLowSchedulerPolicyAfterTouchTimeSeconds;
    336 
    337     releaseStore(&m_schedulerPolicy, schedulerPolicy);
    338     TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.scheduler"), "SchedulerPolicy", schedulerPolicy);
    339 }
    340 
    341 } // namespace blink
    342