message_loop_impl.cc 3.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/message_loop_impl.h"

#include "lib/ftl/build_config.h"

#if OS_MACOSX

#include "flutter/fml/platform/darwin/message_loop_darwin.h"
using PlatformMessageLoopImpl = fml::MessageLoopDarwin;

#elif OS_ANDROID

#include "flutter/fml/platform/android/message_loop_android.h"
using PlatformMessageLoopImpl = fml::MessageLoopAndroid;

#elif OS_LINUX

#include "flutter/fml/platform/linux/message_loop_linux.h"
using PlatformMessageLoopImpl = fml::MessageLoopLinux;

#else

#error This platform does not have a message loop implementation.

#endif

namespace fml {

ftl::RefPtr<MessageLoopImpl> MessageLoopImpl::Create() {
  return ftl::MakeRefCounted<::PlatformMessageLoopImpl>();
}

MessageLoopImpl::MessageLoopImpl() : order_(0), terminated_(false) {}

MessageLoopImpl::~MessageLoopImpl() = default;

void MessageLoopImpl::PostTask(ftl::Closure task, ftl::TimePoint target_time) {
  FTL_DCHECK(task != nullptr);
  WakeUp(RegisterTaskAndGetNextWake(task, target_time));
}

void MessageLoopImpl::RunExpiredTasksNow() {
  WakeUp(RunExpiredTasksAndGetNextWake());
}

void MessageLoopImpl::SetTaskObserver(MessageLoop::TaskObserver observer) {
  FTL_DCHECK(MessageLoop::GetCurrent().GetLoopImpl().get() == this)
      << "Message loop task observer must be set on the same thread as the "
         "loop.";
  task_observer_ = observer;
}

void MessageLoopImpl::DoRun() {
  if (terminated_) {
    // Message loops may be run only once.
    return;
  }

  // Allow the implementation to do its thing.
  Run();

  // The message loop is shutting down. Check if there are expired tasks. This
  // is the last chance for expired tasks to be serviced.
  RunExpiredTasksNow();

  // The loop may have been implicitly terminated. This can happen if the
  // implementation supports termination via platform specific APIs or just
  // error conditions. Set the terminated flag manually.
  terminated_ = true;

  // When the message loop is in the process of shutting down, pending tasks
  // should be destructed on the message loop's thread. We have just returned
  // from the implementations |Run| method which we know is on the correct
  // thread. Drop all pending tasks on the floor.
  ftl::MutexLocker lock(&delayed_tasks_mutex_);
  delayed_tasks_ = {};
}

void MessageLoopImpl::DoTerminate() {
  terminated_ = true;
  Terminate();
}

ftl::TimePoint MessageLoopImpl::RegisterTaskAndGetNextWake(
    ftl::Closure task,
    ftl::TimePoint target_time) {
  if (terminated_) {
    // If the message loop has already been terminated, PostTask should destruct
    // |task| synchronously within this function.
    return ftl::TimePoint::Max();
  }
  FTL_DCHECK(task != nullptr);
  ftl::MutexLocker lock(&delayed_tasks_mutex_);
  delayed_tasks_.push({++order_, std::move(task), target_time});
  return delayed_tasks_.top().target_time;
}

ftl::TimePoint MessageLoopImpl::RunExpiredTasksAndGetNextWake() {
  std::vector<ftl::Closure> invocations;

  {
    ftl::MutexLocker lock(&delayed_tasks_mutex_);

    if (delayed_tasks_.empty()) {
      return ftl::TimePoint::Max();
    }

    auto now = ftl::TimePoint::Now();
    while (!delayed_tasks_.empty()) {
      const auto& top = delayed_tasks_.top();
      if (top.target_time > now) {
        break;
      }
      invocations.emplace_back(std::move(top.task));
      delayed_tasks_.pop();
    }
  }

  for (const auto& invocation : invocations) {
    invocation();
    if (task_observer_) {
      task_observer_();
    }
  }

  ftl::MutexLocker lock(&delayed_tasks_mutex_);
  return delayed_tasks_.empty() ? ftl::TimePoint::Max()
                                : delayed_tasks_.top().target_time;
}

}  // namespace fml