diff --git a/fml/icu_util.cc b/fml/icu_util.cc new file mode 100644 index 0000000000000000000000000000000000000000..d7737b6e88b80c48bff3d47f9b7708b54e784936 --- /dev/null +++ b/fml/icu_util.cc @@ -0,0 +1,93 @@ +// 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/icu_util.h" + +#include +#include + +#include "flutter/fml/mapping.h" +#include "lib/ftl/build_config.h" +#include "lib/ftl/logging.h" +#include "third_party/icu/source/common/unicode/udata.h" + +namespace fml { +namespace icu { + +static constexpr char kIcuDataFileName[] = "icudtl.dat"; + +class ICUContext { + public: + ICUContext(const std::string& icu_data_path) : valid_(false) { + valid_ = SetupMapping(icu_data_path) && SetupICU(); + } + + ~ICUContext() = default; + + bool SetupMapping(const std::string& icu_data_path) { + // Check if the explicit path specified exists. + auto overriden_path_mapping = std::make_unique(icu_data_path); + if (overriden_path_mapping->GetSize() != 0) { + mapping_ = std::move(overriden_path_mapping); + return true; + } + + // Check to see if the mapping is in the resources bundle. + if (PlatformHasResourcesBundle()) { + auto resource = GetResourceMapping(kIcuDataFileName); + if (resource != nullptr && resource->GetSize() != 0) { + mapping_ = std::move(resource); + return true; + } + } + + // Check if the mapping can by directly accessed via a file path. In this + // case, the data file needs to be next to the executable. + auto file = std::make_unique(kIcuDataFileName); + if (file->GetSize() != 0) { + mapping_ = std::move(file); + return true; + } + + return false; + } + + bool SetupICU() { + if (GetSize() == 0) { + return false; + } + + UErrorCode err_code = U_ZERO_ERROR; + udata_setCommonData(GetMapping(), &err_code); + return (err_code == U_ZERO_ERROR); + } + + const uint8_t* GetMapping() const { + return mapping_ ? mapping_->GetMapping() : nullptr; + } + + size_t GetSize() const { return mapping_ ? mapping_->GetSize() : 0; } + + bool IsValid() const { return valid_; } + + private: + bool valid_; + std::unique_ptr mapping_; + + FTL_DISALLOW_COPY_AND_ASSIGN(ICUContext); +}; + +void InitializeICUOnce(const std::string& icu_data_path) { + static ICUContext* context = new ICUContext(icu_data_path); + FTL_CHECK(context->IsValid()) << "Must be able to initialize the ICU context"; +} + +std::once_flag g_icu_init_flag; +void InitializeICU(const std::string& icu_data_path) { + std::call_once(g_icu_init_flag, + [&icu_data_path]() { InitializeICUOnce(icu_data_path); }); +} + +} // namespace icu +} // namespace fml diff --git a/fml/icu_util.h b/fml/icu_util.h new file mode 100644 index 0000000000000000000000000000000000000000..ab3c3af2e8f4d79965ee0a1085f3109478c8d93f --- /dev/null +++ b/fml/icu_util.h @@ -0,0 +1,20 @@ +// 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. + +#ifndef FLUTTER_FML_ICU_UTIL_H_ +#define FLUTTER_FML_ICU_UTIL_H_ + +#include + +#include "lib/ftl/macros.h" + +namespace fml { +namespace icu { + +void InitializeICU(const std::string& icu_data_path = ""); + +} // namespace icu +} // namespace fml + +#endif // FLUTTER_FML_ICU_UTIL_H_ diff --git a/fml/mapping.cc b/fml/mapping.cc new file mode 100644 index 0000000000000000000000000000000000000000..a584ccd426d80e893a2f5f977db47c75fd9d01bd --- /dev/null +++ b/fml/mapping.cc @@ -0,0 +1,87 @@ +// 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/mapping.h" + +#include +#include +#include +#include + +#include + +#include "lib/ftl/build_config.h" +#include "lib/ftl/files/eintr_wrapper.h" + +#if OS_MACOSX + +#include "flutter/fml/platform/darwin/resource_mapping_darwin.h" +using PlatformResourceMapping = fml::ResourceMappingDarwin; + +#else + +using PlatformResourceMapping = fml::FileMapping; + +#endif + +namespace fml { + +Mapping::Mapping() = default; + +Mapping::~Mapping() = default; + +bool PlatformHasResourcesBundle() { + return !std::is_same::value; +} + +std::unique_ptr GetResourceMapping(const std::string& resource_name) { + return std::make_unique(resource_name); +} + +FileMapping::FileMapping(const std::string& path) + : FileMapping(ftl::UniqueFD{HANDLE_EINTR(::open(path.c_str(), O_RDONLY))}) { +} + +FileMapping::FileMapping(const ftl::UniqueFD& handle) + : size_(0), mapping_(nullptr) { + if (!handle.is_valid()) { + return; + } + + struct stat stat_buffer = {}; + + if (::fstat(handle.get(), &stat_buffer) != 0) { + return; + } + + if (stat_buffer.st_size <= 0) { + return; + } + + auto mapping = ::mmap(nullptr, stat_buffer.st_size, PROT_READ, MAP_PRIVATE, + handle.get(), 0); + + if (mapping == MAP_FAILED) { + return; + } + + mapping_ = static_cast(mapping); + size_ = stat_buffer.st_size; +} + +FileMapping::~FileMapping() { + if (mapping_ != nullptr) { + ::munmap(mapping_, size_); + } +} + +size_t FileMapping::GetSize() const { + return size_; +} + +const uint8_t* FileMapping::GetMapping() const { + return mapping_; +} + +} // namespace fml diff --git a/fml/mapping.h b/fml/mapping.h new file mode 100644 index 0000000000000000000000000000000000000000..da3f050601c44284a7c4c1fbabdff8637a905688 --- /dev/null +++ b/fml/mapping.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef FLUTTER_FML_MAPPING_H_ +#define FLUTTER_FML_MAPPING_H_ + +#include + +#include "lib/ftl/files/unique_fd.h" +#include "lib/ftl/macros.h" + +namespace fml { + +class Mapping { + public: + Mapping(); + + virtual ~Mapping(); + + virtual size_t GetSize() const = 0; + + virtual const uint8_t* GetMapping() const = 0; + + private: + FTL_DISALLOW_COPY_AND_ASSIGN(Mapping); +}; + +bool PlatformHasResourcesBundle(); + +std::unique_ptr GetResourceMapping(const std::string& resource_name); + +class FileMapping : public Mapping { + public: + FileMapping(const std::string& path); + + FileMapping(const ftl::UniqueFD& fd); + + ~FileMapping() override; + + size_t GetSize() const override; + + const uint8_t* GetMapping() const override; + + private: + size_t size_; + uint8_t* mapping_; + + FTL_DISALLOW_COPY_AND_ASSIGN(FileMapping); +}; + +} // namespace fml + +#endif // FLUTTER_FML_MAPPING_H_ diff --git a/fml/message_loop.cc b/fml/message_loop.cc new file mode 100644 index 0000000000000000000000000000000000000000..d2888d75e55ee3dfe708726a7207aadace6c03af --- /dev/null +++ b/fml/message_loop.cc @@ -0,0 +1,70 @@ +// 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.h" + +#include + +#include "flutter/fml/message_loop_impl.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/thread_local.h" +#include "lib/ftl/memory/ref_counted.h" +#include "lib/ftl/memory/ref_ptr.h" + +namespace fml { + +FML_THREAD_LOCAL ThreadLocal tls_message_loop([](intptr_t value) { + delete reinterpret_cast(value); +}); + +MessageLoop& MessageLoop::GetCurrent() { + auto loop = reinterpret_cast(tls_message_loop.Get()); + FTL_CHECK(loop != nullptr) + << "MessageLoop::EnsureInitializedForCurrentThread was not called on " + "this thread prior to message loop use."; + return *loop; +} + +void MessageLoop::EnsureInitializedForCurrentThread() { + if (tls_message_loop.Get() != 0) { + // Already initialized. + return; + } + tls_message_loop.Set(reinterpret_cast(new MessageLoop())); +} + +bool MessageLoop::IsInitializedForCurrentThread() { + return tls_message_loop.Get() != 0; +} + +MessageLoop::MessageLoop() + : loop_(MessageLoopImpl::Create()), + task_runner_(ftl::MakeRefCounted(loop_)) { + FTL_CHECK(loop_); + FTL_CHECK(task_runner_); +} + +MessageLoop::~MessageLoop() = default; + +void MessageLoop::Run() { + loop_->DoRun(); +} + +void MessageLoop::Terminate() { + loop_->DoTerminate(); +} + +ftl::RefPtr MessageLoop::GetTaskRunner() const { + return task_runner_; +} + +ftl::RefPtr MessageLoop::GetLoopImpl() const { + return loop_; +} + +void MessageLoop::SetTaskObserver(TaskObserver observer) { + loop_->SetTaskObserver(std::move(observer)); +} + +} // namespace fml diff --git a/fml/message_loop.h b/fml/message_loop.h new file mode 100644 index 0000000000000000000000000000000000000000..04853deb0df784b48e0e389334c2b8f9a078cbe9 --- /dev/null +++ b/fml/message_loop.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef FLUTTER_FML_MESSAGE_LOOP_H_ +#define FLUTTER_FML_MESSAGE_LOOP_H_ + +#include "lib/ftl/macros.h" +#include "lib/ftl/tasks/task_runner.h" + +namespace fml { + +class TaskRunner; +class MessageLoopImpl; + +class MessageLoop { + public: + static MessageLoop& GetCurrent(); + + bool IsValid() const; + + void Run(); + + void Terminate(); + + using TaskObserver = std::function; + + void SetTaskObserver(TaskObserver observer); + + ftl::RefPtr GetTaskRunner() const; + + static void EnsureInitializedForCurrentThread(); + + static bool IsInitializedForCurrentThread(); + + ~MessageLoop(); + + private: + friend class TaskRunner; + friend class MessageLoopImpl; + + ftl::RefPtr loop_; + ftl::RefPtr task_runner_; + + MessageLoop(); + + ftl::RefPtr GetLoopImpl() const; + + FTL_DISALLOW_COPY_AND_ASSIGN(MessageLoop); +}; + +} // namespace fml + +#endif // FLUTTER_FML_MESSAGE_LOOP_H_ diff --git a/fml/message_loop_impl.cc b/fml/message_loop_impl.cc new file mode 100644 index 0000000000000000000000000000000000000000..eaaa3b5a950796174fae30a1177e639d747c978b --- /dev/null +++ b/fml/message_loop_impl.cc @@ -0,0 +1,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::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 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 diff --git a/fml/message_loop_impl.h b/fml/message_loop_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..b018b2fa1b98eeb50b8c6a2ac958a54aafdffd9c --- /dev/null +++ b/fml/message_loop_impl.h @@ -0,0 +1,88 @@ +// 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. + +#ifndef FLUTTER_FML_MESSAGE_LOOP_IMPL_H_ +#define FLUTTER_FML_MESSAGE_LOOP_IMPL_H_ + +#include +#include +#include +#include + +#include "flutter/fml/message_loop.h" +#include "lib/ftl/functional/closure.h" +#include "lib/ftl/macros.h" +#include "lib/ftl/memory/ref_counted.h" +#include "lib/ftl/synchronization/mutex.h" +#include "lib/ftl/synchronization/thread_annotations.h" +#include "lib/ftl/time/time_point.h" + +namespace fml { + +class MessageLoopImpl : public ftl::RefCountedThreadSafe { + public: + static ftl::RefPtr Create(); + + virtual ~MessageLoopImpl(); + + virtual void Run() = 0; + + virtual void Terminate() = 0; + + virtual void WakeUp(ftl::TimePoint time_point) = 0; + + void PostTask(ftl::Closure task, ftl::TimePoint target_time); + + void SetTaskObserver(MessageLoop::TaskObserver observer); + + void DoRun(); + + void DoTerminate(); + + protected: + MessageLoopImpl(); + + void RunExpiredTasksNow(); + + private: + struct DelayedTask { + size_t order; + ftl::Closure task; + ftl::TimePoint target_time; + + DelayedTask(size_t p_order, + ftl::Closure p_task, + ftl::TimePoint p_target_time) + : order(p_order), task(std::move(p_task)), target_time(p_target_time) {} + }; + + struct DelayedTaskCompare { + bool operator()(const DelayedTask& a, const DelayedTask& b) { + return a.target_time == b.target_time ? a.order > b.order + : a.target_time > b.target_time; + } + }; + + using DelayedTaskQueue = std:: + priority_queue, DelayedTaskCompare>; + + MessageLoop::TaskObserver task_observer_; + ftl::Mutex delayed_tasks_mutex_; + DelayedTaskQueue delayed_tasks_ FTL_GUARDED_BY(delayed_tasks_mutex_); + size_t order_ FTL_GUARDED_BY(delayed_tasks_mutex_); + std::atomic_bool terminated_; + + FTL_WARN_UNUSED_RESULT + ftl::TimePoint RegisterTaskAndGetNextWake(ftl::Closure task, + ftl::TimePoint target_time); + + FTL_WARN_UNUSED_RESULT + ftl::TimePoint RunExpiredTasksAndGetNextWake(); + + FTL_DISALLOW_COPY_AND_ASSIGN(MessageLoopImpl); +}; + +} // namespace fml + +#endif // FLUTTER_FML_MESSAGE_LOOP_IMPL_H_ diff --git a/fml/message_loop_unittests.cc b/fml/message_loop_unittests.cc new file mode 100644 index 0000000000000000000000000000000000000000..ea4d2781c0fbb5a6ca69ac81ee85d61c7d23b5ed --- /dev/null +++ b/fml/message_loop_unittests.cc @@ -0,0 +1,269 @@ +// 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 + +#include "flutter/fml/message_loop.h" +#include "gtest/gtest.h" +#include "lib/ftl/synchronization/waitable_event.h" + +#define TIME_SENSITIVE(x) TimeSensitiveTest_##x + +TEST(MessageLoop, GetCurrent) { + std::thread thread([]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + ASSERT_TRUE(fml::MessageLoop::GetCurrent().GetTaskRunner()); + }); + thread.join(); +} + +TEST(MessageLoop, DifferentThreadsHaveDifferentLoops) { + fml::MessageLoop* loop1 = nullptr; + ftl::AutoResetWaitableEvent latch1; + ftl::AutoResetWaitableEvent term1; + std::thread thread1([&loop1, &latch1, &term1]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + loop1 = &fml::MessageLoop::GetCurrent(); + latch1.Signal(); + term1.Wait(); + }); + + fml::MessageLoop* loop2 = nullptr; + ftl::AutoResetWaitableEvent latch2; + ftl::AutoResetWaitableEvent term2; + std::thread thread2([&loop2, &latch2, &term2]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + loop2 = &fml::MessageLoop::GetCurrent(); + latch2.Signal(); + term2.Wait(); + }); + latch1.Wait(); + latch2.Wait(); + ASSERT_FALSE(loop1 == loop2); + term1.Signal(); + term2.Signal(); + thread1.join(); + thread2.join(); +} + +TEST(MessageLoop, CanRunAndTerminate) { + bool started = false; + bool terminated = false; + std::thread thread([&started, &terminated]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + ASSERT_TRUE(loop.GetTaskRunner()); + loop.GetTaskRunner()->PostTask([&terminated]() { + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + }); + loop.Run(); + started = true; + }); + thread.join(); + ASSERT_TRUE(started); + ASSERT_TRUE(terminated); +} + +TEST(MessageLoop, NonDelayedTasksAreRunInOrder) { + const size_t count = 100; + bool started = false; + bool terminated = false; + std::thread thread([&started, &terminated, count]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + size_t current = 0; + for (size_t i = 0; i < count; i++) { + loop.GetTaskRunner()->PostTask([&terminated, count, i, ¤t]() { + ASSERT_EQ(current, i); + current++; + if (count == i + 1) { + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + } + }); + } + loop.Run(); + ASSERT_EQ(current, count); + started = true; + }); + thread.join(); + ASSERT_TRUE(started); + ASSERT_TRUE(terminated); +} + +TEST(MessageLoop, DelayedTasksAtSameTimeAreRunInOrder) { + const size_t count = 100; + bool started = false; + bool terminated = false; + std::thread thread([&started, &terminated, count]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + size_t current = 0; + const auto now_plus_some = + ftl::TimePoint::Now() + ftl::TimeDelta::FromMilliseconds(2); + for (size_t i = 0; i < count; i++) { + loop.GetTaskRunner()->PostTaskForTime( + [&terminated, count, i, ¤t]() { + ASSERT_EQ(current, i); + current++; + if (count == i + 1) { + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + } + }, + now_plus_some); + } + loop.Run(); + ASSERT_EQ(current, count); + started = true; + }); + thread.join(); + ASSERT_TRUE(started); + ASSERT_TRUE(terminated); +} + +TEST(MessageLoop, CheckRunsTaskOnCurrentThread) { + ftl::RefPtr runner; + ftl::AutoResetWaitableEvent latch; + std::thread thread([&runner, &latch]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + runner = loop.GetTaskRunner(); + latch.Signal(); + ASSERT_TRUE(loop.GetTaskRunner()->RunsTasksOnCurrentThread()); + }); + latch.Wait(); + ASSERT_TRUE(runner); + ASSERT_FALSE(runner->RunsTasksOnCurrentThread()); + thread.join(); +} + +TEST(MessageLoop, TIME_SENSITIVE(SingleDelayedTaskByDelta)) { + bool checked = false; + std::thread thread([&checked]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + auto begin = ftl::TimePoint::Now(); + loop.GetTaskRunner()->PostDelayedTask( + [begin, &checked]() { + auto delta = ftl::TimePoint::Now() - begin; + auto ms = delta.ToMillisecondsF(); + ASSERT_GE(ms, 3); + ASSERT_LE(ms, 7); + checked = true; + fml::MessageLoop::GetCurrent().Terminate(); + }, + ftl::TimeDelta::FromMilliseconds(5)); + loop.Run(); + }); + thread.join(); + ASSERT_TRUE(checked); +} + +TEST(MessageLoop, TIME_SENSITIVE(SingleDelayedTaskForTime)) { + bool checked = false; + std::thread thread([&checked]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + auto begin = ftl::TimePoint::Now(); + loop.GetTaskRunner()->PostTaskForTime( + [begin, &checked]() { + auto delta = ftl::TimePoint::Now() - begin; + auto ms = delta.ToMillisecondsF(); + ASSERT_GE(ms, 3); + ASSERT_LE(ms, 7); + checked = true; + fml::MessageLoop::GetCurrent().Terminate(); + }, + ftl::TimePoint::Now() + ftl::TimeDelta::FromMilliseconds(5)); + loop.Run(); + }); + thread.join(); + ASSERT_TRUE(checked); +} + +TEST(MessageLoop, TIME_SENSITIVE(MultipleDelayedTasksWithIncreasingDeltas)) { + const auto count = 10; + int checked = false; + std::thread thread([&checked, count]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + for (int target_ms = 0 + 2; target_ms < count + 2; target_ms++) { + auto begin = ftl::TimePoint::Now(); + loop.GetTaskRunner()->PostDelayedTask( + [begin, target_ms, &checked]() { + auto delta = ftl::TimePoint::Now() - begin; + auto ms = delta.ToMillisecondsF(); + ASSERT_GE(ms, target_ms - 2); + ASSERT_LE(ms, target_ms + 2); + checked++; + if (checked == count) { + fml::MessageLoop::GetCurrent().Terminate(); + } + }, + ftl::TimeDelta::FromMilliseconds(target_ms)); + } + loop.Run(); + }); + thread.join(); + ASSERT_EQ(checked, count); +} + +TEST(MessageLoop, TIME_SENSITIVE(MultipleDelayedTasksWithDecreasingDeltas)) { + const auto count = 10; + int checked = false; + std::thread thread([&checked, count]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = fml::MessageLoop::GetCurrent(); + for (int target_ms = count + 2; target_ms >= 0 + 2; target_ms--) { + auto begin = ftl::TimePoint::Now(); + loop.GetTaskRunner()->PostDelayedTask( + [begin, target_ms, &checked]() { + auto delta = ftl::TimePoint::Now() - begin; + auto ms = delta.ToMillisecondsF(); + ASSERT_GE(ms, target_ms - 2); + ASSERT_LE(ms, target_ms + 2); + checked++; + if (checked == count) { + fml::MessageLoop::GetCurrent().Terminate(); + } + }, + ftl::TimeDelta::FromMilliseconds(target_ms)); + } + loop.Run(); + }); + thread.join(); + ASSERT_EQ(checked, count); +} + +TEST(MessageLoop, TaskObserverFire) { + bool started = false; + bool terminated = false; + std::thread thread([&started, &terminated]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + const size_t count = 25; + auto& loop = fml::MessageLoop::GetCurrent(); + size_t task_count = 0; + size_t obs_count = 0; + loop.SetTaskObserver([&obs_count]() { obs_count++; }); + for (size_t i = 0; i < count; i++) { + loop.GetTaskRunner()->PostTask([&terminated, count, i, &task_count]() { + ASSERT_EQ(task_count, i); + task_count++; + if (count == i + 1) { + fml::MessageLoop::GetCurrent().Terminate(); + terminated = true; + } + }); + } + loop.Run(); + ASSERT_EQ(task_count, count); + ASSERT_EQ(obs_count, count); + started = true; + }); + thread.join(); + ASSERT_TRUE(started); + ASSERT_TRUE(terminated); +} diff --git a/fml/task_runner.cc b/fml/task_runner.cc new file mode 100644 index 0000000000000000000000000000000000000000..bc85571845db585fcf43fd149bdd4421c22e0146 --- /dev/null +++ b/fml/task_runner.cc @@ -0,0 +1,41 @@ +// 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/task_runner.h" + +#include + +#include "flutter/fml/message_loop.h" +#include "flutter/fml/message_loop_impl.h" + +namespace fml { + +TaskRunner::TaskRunner(ftl::RefPtr loop) + : loop_(std::move(loop)) { + FTL_CHECK(loop_); +} + +TaskRunner::~TaskRunner() = default; + +void TaskRunner::PostTask(ftl::Closure task) { + loop_->PostTask(std::move(task), ftl::TimePoint::Now()); +} + +void TaskRunner::PostTaskForTime(ftl::Closure task, + ftl::TimePoint target_time) { + loop_->PostTask(std::move(task), target_time); +} + +void TaskRunner::PostDelayedTask(ftl::Closure task, ftl::TimeDelta delay) { + loop_->PostTask(std::move(task), ftl::TimePoint::Now() + delay); +} + +bool TaskRunner::RunsTasksOnCurrentThread() { + if (!fml::MessageLoop::IsInitializedForCurrentThread()) { + return false; + } + return MessageLoop::GetCurrent().GetLoopImpl() == loop_; +} + +} // namespace fml diff --git a/fml/task_runner.h b/fml/task_runner.h new file mode 100644 index 0000000000000000000000000000000000000000..d70c02b022f8a024cad9e5591437cf4b2711adb9 --- /dev/null +++ b/fml/task_runner.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef FLUTTER_FML_TASK_RUNNER_H_ +#define FLUTTER_FML_TASK_RUNNER_H_ + +#include "lib/ftl/macros.h" +#include "lib/ftl/memory/ref_counted.h" +#include "lib/ftl/tasks/task_runner.h" + +namespace fml { + +class MessageLoopImpl; + +class TaskRunner : public ftl::TaskRunner { + public: + void PostTask(ftl::Closure task) override; + + void PostTaskForTime(ftl::Closure task, ftl::TimePoint target_time) override; + + void PostDelayedTask(ftl::Closure task, ftl::TimeDelta delay) override; + + bool RunsTasksOnCurrentThread() override; + + private: + ftl::RefPtr loop_; + + TaskRunner(ftl::RefPtr loop); + + ~TaskRunner(); + + FRIEND_MAKE_REF_COUNTED(TaskRunner); + FRIEND_REF_COUNTED_THREAD_SAFE(TaskRunner); + FTL_DISALLOW_COPY_AND_ASSIGN(TaskRunner); +}; + +} // namespace fml + +#endif // FLUTTER_FML_TASK_RUNNER_H_ diff --git a/fml/thread.cc b/fml/thread.cc new file mode 100644 index 0000000000000000000000000000000000000000..62ff298cf24cb646ba4af04207835d02850aea96 --- /dev/null +++ b/fml/thread.cc @@ -0,0 +1,69 @@ +// 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/thread.h" + +#include +#include + +#include "flutter/fml/message_loop.h" +#include "lib/ftl/build_config.h" +#include "lib/ftl/synchronization/waitable_event.h" + +#if OS_MACOSX +#include +#elif OS_LINUX || OS_ANDROID +#include +#else +#error Unsupported Platform +#endif + +namespace fml { + +Thread::Thread(const std::string& name) : joined_(false) { + ftl::AutoResetWaitableEvent latch; + ftl::RefPtr runner; + thread_ = std::make_unique([&latch, &runner, name]() -> void { + SetCurrentThreadName(name); + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto& loop = MessageLoop::GetCurrent(); + runner = loop.GetTaskRunner(); + latch.Signal(); + loop.Run(); + }); + latch.Wait(); + task_runner_ = runner; +} + +Thread::~Thread() { + Join(); +} + +ftl::RefPtr Thread::GetTaskRunner() const { + return task_runner_; +} + +void Thread::Join() { + if (joined_) { + return; + } + joined_ = true; + task_runner_->PostTask([]() { MessageLoop::GetCurrent().Terminate(); }); + thread_->join(); +} + +void Thread::SetCurrentThreadName(const std::string& name) { + if (name == "") { + return; + } +#if OS_MACOSX + pthread_setname_np(name.c_str()); +#elif OS_LINUX || OS_ANDROID + pthread_setname_np(pthread_self(), name.c_str()); +#else +#error Unsupported Platform +#endif +} + +} // namespace fml diff --git a/fml/thread.h b/fml/thread.h new file mode 100644 index 0000000000000000000000000000000000000000..83b0fca7045547a7d094622e1775611e8858bba4 --- /dev/null +++ b/fml/thread.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef FLUTTER_FML_THREAD_H_ +#define FLUTTER_FML_THREAD_H_ + +#include +#include +#include + +#include "lib/ftl/macros.h" +#include "lib/ftl/tasks/task_runner.h" + +namespace fml { + +class Thread { + public: + explicit Thread(const std::string& name = ""); + + ~Thread(); + + ftl::RefPtr GetTaskRunner() const; + + void Join(); + + private: + std::unique_ptr thread_; + ftl::RefPtr task_runner_; + std::atomic_bool joined_; + + static void SetCurrentThreadName(const std::string& name); + + FTL_DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +} // namespace fml + +#endif // FLUTTER_FML_THREAD_H_ diff --git a/fml/thread_checker.cc b/fml/thread_checker.cc new file mode 100644 index 0000000000000000000000000000000000000000..d359c5cf74b3b3fac434b379ee84634ae235ec3d --- /dev/null +++ b/fml/thread_checker.cc @@ -0,0 +1,17 @@ +// 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/thread_checker.h" + +namespace fml { + +ThreadChecker::ThreadChecker() : handle_(std::this_thread::get_id()) {} + +ThreadChecker::~ThreadChecker() = default; + +bool ThreadChecker::IsCalledOnValidThread() const { + return handle_ == std::this_thread::get_id(); +} + +} // namespace fml diff --git a/fml/thread_checker.h b/fml/thread_checker.h new file mode 100644 index 0000000000000000000000000000000000000000..b0d5400bdbf8c71f5e83c1dcdafeddb0004d99b2 --- /dev/null +++ b/fml/thread_checker.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef FLUTTER_FML_THREAD_CHECKER_H_ +#define FLUTTER_FML_THREAD_CHECKER_H_ + +#include + +#include "lib/ftl/macros.h" + +namespace fml { + +class ThreadChecker { + public: + ThreadChecker(); + + ~ThreadChecker(); + + bool IsCalledOnValidThread() const; + + private: + const std::thread::id handle_; + + FTL_DISALLOW_COPY_AND_ASSIGN(ThreadChecker); +}; + +} // namespace fml + +#endif // FLUTTER_FML_THREAD_CHECKER_H_ diff --git a/fml/thread_checker_unittests.cc b/fml/thread_checker_unittests.cc new file mode 100644 index 0000000000000000000000000000000000000000..0aa9d8a87974c5ffc7b1fe94e075b39e084532f3 --- /dev/null +++ b/fml/thread_checker_unittests.cc @@ -0,0 +1,16 @@ +// 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 + +#include "flutter/fml/thread_checker.h" +#include "gtest/gtest.h" + +TEST(ThreadChecker, CheckCalledOnValidThread) { + fml::ThreadChecker checker; + ASSERT_TRUE(checker.IsCalledOnValidThread()); + std::thread thread( + [&checker]() { ASSERT_FALSE(checker.IsCalledOnValidThread()); }); + thread.join(); +} diff --git a/fml/thread_local.h b/fml/thread_local.h new file mode 100644 index 0000000000000000000000000000000000000000..1792b6bc03c376bc2a28f97b28a2978508b80ff1 --- /dev/null +++ b/fml/thread_local.h @@ -0,0 +1,160 @@ +// 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. + +#ifndef FLUTTER_FML_THREAD_LOCAL_H_ +#define FLUTTER_FML_THREAD_LOCAL_H_ + +#include + +#include "lib/ftl/build_config.h" +#include "lib/ftl/logging.h" +#include "lib/ftl/macros.h" + +#define FML_THREAD_LOCAL_PTHREADS OS_MACOSX || OS_LINUX || OS_ANDROID + +#if FML_THREAD_LOCAL_PTHREADS +#include +#endif + +namespace fml { + +using ThreadLocalDestroyCallback = std::function; + +#if FML_THREAD_LOCAL_PTHREADS + +// thread_local is unavailable and we have to resort to pthreads. + +#define FML_THREAD_LOCAL static + +class ThreadLocal { + private: + class Box { + public: + Box(ThreadLocalDestroyCallback destroy, intptr_t value) + : destroy_(destroy), value_(value) {} + + intptr_t Value() const { return value_; } + + void SetValue(intptr_t value) { + if (value == value_) { + return; + } + + DestroyValue(); + value_ = value; + } + + void DestroyValue() { + if (destroy_) { + destroy_(value_); + } + } + + private: + ThreadLocalDestroyCallback destroy_; + intptr_t value_; + + FTL_DISALLOW_COPY_AND_ASSIGN(Box); + }; + + static inline void ThreadLocalDestroy(void* value) { + FTL_CHECK(value != nullptr); + auto box = reinterpret_cast(value); + box->DestroyValue(); + delete box; + } + + public: + ThreadLocal() : ThreadLocal(nullptr) {} + + ThreadLocal(ThreadLocalDestroyCallback destroy) : destroy_(destroy) { + auto callback = + reinterpret_cast(&ThreadLocal::ThreadLocalDestroy); + FTL_CHECK(pthread_key_create(&_key, callback) == 0); + } + + void Set(intptr_t value) { + auto box = reinterpret_cast(pthread_getspecific(_key)); + if (box == nullptr) { + box = new Box(destroy_, value); + FTL_CHECK(pthread_setspecific(_key, box) == 0); + } else { + box->SetValue(value); + } + } + + intptr_t Get() { + auto box = reinterpret_cast(pthread_getspecific(_key)); + return box != nullptr ? box->Value() : 0; + } + + ~ThreadLocal() { + // This will NOT call the destroy callbacks on thread local values still + // active in other threads. Those must be cleared manually. The usage + // of this class should be similar to the thread_local keyword but with + // with a static storage specifier + + // Collect the container + delete reinterpret_cast(pthread_getspecific(_key)); + + // Finally, collect the key + FTL_CHECK(pthread_key_delete(_key) == 0); + } + + private: + pthread_key_t _key; + ThreadLocalDestroyCallback destroy_; + + FTL_DISALLOW_COPY_AND_ASSIGN(ThreadLocal); +}; + +#else // FML_THREAD_LOCAL_PTHREADS + +#define FML_THREAD_LOCAL thread_local + +class ThreadLocal { + public: + ThreadLocal() : ThreadLocal(nullptr) {} + + ThreadLocal(ThreadLocalDestroyCallback destroy) + : destroy_(destroy), value_(0) {} + + void Set(intptr_t value) { + if (value_ == value) { + return; + } + + if (value_ != 0 && destroy_) { + destroy_(value_); + } + + value_ = value; + } + + intptr_t Get() { return value_; } + + ~ThreadLocal() { + if (value_ != 0 && destroy_) { + destroy_(value_); + } + } + + private: + ThreadLocalDestroyCallback destroy_; + intptr_t value_; + + FTL_DISALLOW_COPY_AND_ASSIGN(ThreadLocal); +}; + +#endif // FML_THREAD_LOCAL_PTHREADS + +#ifndef FML_THREAD_LOCAL + +#error Thread local storage unavailable on the platform. + +#endif // FML_THREAD_LOCAL + +} // namespace fml + +#endif // FLUTTER_FML_THREAD_LOCAL_H_ diff --git a/fml/thread_local_unittests.cc b/fml/thread_local_unittests.cc new file mode 100644 index 0000000000000000000000000000000000000000..70d84300f69ae6450290583b604a5202096ae2c2 --- /dev/null +++ b/fml/thread_local_unittests.cc @@ -0,0 +1,108 @@ +// 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 + +#include "flutter/fml/thread_local.h" +#include "gtest/gtest.h" + +// We are only going to test the pthreads based thread local boxes. +#if FML_THREAD_LOCAL_PTHREADS + +TEST(ThreadLocal, SimpleInitialization) { + std::thread thread([&] { + fml::ThreadLocal local; + auto value = 100; + local.Set(value); + ASSERT_EQ(local.Get(), value); + }); + thread.join(); +} + +TEST(ThreadLocal, SimpleInitializationCheckInAnother) { + std::thread thread([&] { + fml::ThreadLocal local; + auto value = 100; + local.Set(value); + ASSERT_EQ(local.Get(), value); + std::thread thread2([&]() { ASSERT_EQ(local.Get(), 0); }); + thread2.join(); + }); + thread.join(); +} + +TEST(ThreadLocal, DestroyCallback) { + std::thread thread([&] { + int destroys = 0; + fml::ThreadLocal local([&destroys](intptr_t) { destroys++; }); + auto value = 100; + local.Set(value); + ASSERT_EQ(local.Get(), value); + ASSERT_EQ(destroys, 0); + }); + thread.join(); +} + +TEST(ThreadLocal, DestroyCallback2) { + std::thread thread([&] { + int destroys = 0; + fml::ThreadLocal local([&destroys](intptr_t) { destroys++; }); + + local.Set(100); + ASSERT_EQ(local.Get(), 100); + ASSERT_EQ(destroys, 0); + local.Set(200); + ASSERT_EQ(local.Get(), 200); + ASSERT_EQ(destroys, 1); + }); + thread.join(); +} + +TEST(ThreadLocal, DestroyThreadTimeline) { + std::thread thread([&] { + int destroys = 0; + fml::ThreadLocal local([&destroys](intptr_t) { destroys++; }); + + std::thread thread([&]() { + local.Set(100); + ASSERT_EQ(local.Get(), 100); + ASSERT_EQ(destroys, 0); + local.Set(200); + ASSERT_EQ(local.Get(), 200); + ASSERT_EQ(destroys, 1); + }); + ASSERT_EQ(local.Get(), 0); + thread.join(); + ASSERT_EQ(local.Get(), 0); + ASSERT_EQ(destroys, 2); + }); + thread.join(); +} + +TEST(ThreadLocal, SettingSameValue) { + std::thread thread([&] { + int destroys = 0; + { + fml::ThreadLocal local([&destroys](intptr_t) { destroys++; }); + + local.Set(100); + ASSERT_EQ(destroys, 0); + local.Set(100); + local.Set(100); + local.Set(100); + ASSERT_EQ(local.Get(), 100); + local.Set(100); + local.Set(100); + ASSERT_EQ(destroys, 0); + local.Set(200); + ASSERT_EQ(destroys, 1); + ASSERT_EQ(local.Get(), 200); + } + + ASSERT_EQ(destroys, 1); + }); + thread.join(); +} + +#endif // FML_THREAD_LOCAL_PTHREADS diff --git a/fml/thread_unittests.cc b/fml/thread_unittests.cc new file mode 100644 index 0000000000000000000000000000000000000000..c4ee56779dab4fd915e5e97845a86dacd68a8682 --- /dev/null +++ b/fml/thread_unittests.cc @@ -0,0 +1,26 @@ +// 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 "gtest/gtest.h" + +#include "flutter/fml/thread.h" + +TEST(Thread, CanStartAndEnd) { + fml::Thread thread; + ASSERT_TRUE(thread.GetTaskRunner()); +} + +TEST(Thread, CanStartAndEndWithExplicitJoin) { + fml::Thread thread; + ASSERT_TRUE(thread.GetTaskRunner()); + thread.Join(); +} + +TEST(Thread, HasARunningMessageLoop) { + fml::Thread thread; + bool done = false; + thread.GetTaskRunner()->PostTask([&done]() { done = true; }); + thread.Join(); + ASSERT_TRUE(done); +} diff --git a/fml/trace_event.cc b/fml/trace_event.cc new file mode 100644 index 0000000000000000000000000000000000000000..8e91d0c95f41bdaf48764eb5d0b80ccf1bc29228 --- /dev/null +++ b/fml/trace_event.cc @@ -0,0 +1,140 @@ +// 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/trace_event.h" + +#include "dart/runtime/include/dart_tools_api.h" + +namespace fml { +namespace tracing { + +void TraceEvent0(TraceArg category_group, TraceArg name) { + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Begin, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); +} + +void TraceEvent1(TraceArg category_group, + TraceArg name, + TraceArg arg1_name, + TraceArg arg1_val) { + const char* arg_names[] = {arg1_name}; + const char* arg_values[] = {arg1_val}; + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Begin, // event type + 1, // argument_count + arg_names, // argument_names + arg_values // argument_values + ); +} + +void TraceEvent2(TraceArg category_group, + TraceArg name, + TraceArg arg1_name, + TraceArg arg1_val, + TraceArg arg2_name, + TraceArg arg2_val) { + const char* arg_names[] = {arg1_name, arg2_name}; + const char* arg_values[] = {arg1_val, arg2_val}; + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Begin, // event type + 2, // argument_count + arg_names, // argument_names + arg_values // argument_values + ); +} + +void TraceEventEnd(TraceArg name) { + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_End, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); +} + +void TraceEventAsyncBegin0(TraceArg category_group, + TraceArg name, + TraceIDArg id) { + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_Begin, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); +} + +void TraceEventAsyncEnd0(TraceArg category_group, + TraceArg name, + TraceIDArg id) { + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_End, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); +} + +void TraceEventAsyncBegin1(TraceArg category_group, + TraceArg name, + TraceIDArg id, + TraceArg arg1_name, + TraceArg arg1_val) { + const char* arg_names[] = {arg1_name}; + const char* arg_values[] = {arg1_val}; + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_Begin, // event type + 1, // argument_count + arg_names, // argument_names + arg_values // argument_values + ); +} + +void TraceEventAsyncEnd1(TraceArg category_group, + TraceArg name, + TraceIDArg id, + TraceArg arg1_name, + TraceArg arg1_val) { + const char* arg_names[] = {arg1_name}; + const char* arg_values[] = {arg1_val}; + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_End, // event type + 1, // argument_count + arg_names, // argument_names + arg_values // argument_values + ); +} + +void TraceEventInstant0(TraceArg category_group, TraceArg name) { + Dart_TimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Instant, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); +} + +} // namespace tracing +} // namespace fml diff --git a/fml/trace_event.h b/fml/trace_event.h new file mode 100644 index 0000000000000000000000000000000000000000..48bbd3b86a4c8b7fd1a12251b8fbda8b56137b50 --- /dev/null +++ b/fml/trace_event.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef FLUTTER_FML_TRACE_EVENT_H_ +#define FLUTTER_FML_TRACE_EVENT_H_ + +#include +#include +#include + +#include "lib/ftl/macros.h" + +#define TRACE_EVENT0(category_group, name) \ + ::fml::tracing::TraceEvent0(category_group, name); \ + ::fml::tracing::ScopedInstantEnd __trace_end0_##__LINE__(name); + +#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val) \ + ::fml::tracing::TraceEvent1(category_group, name, arg1_name, arg1_val); \ + ::fml::tracing::ScopedInstantEnd __trace_end1_##__LINE__(name); + +#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + ::fml::tracing::TraceEvent2(category_group, name, arg1_name, arg1_val, \ + arg2_name, arg2_val); \ + ::fml::tracing::ScopedInstantEnd __trace_end2_##__LINE__(name); + +#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id) \ + ::fml::tracing::TraceEventAsyncBegin0(category_group, name, id); + +#define TRACE_EVENT_ASYNC_END0(category_group, name, id) \ + ::fml::tracing::TraceEventAsyncEnd0(category_group, name, id); + +#define TRACE_EVENT_ASYNC_BEGIN1(category_group, name, id, arg1_name, \ + arg1_val) \ + ::fml::tracing::TraceEventAsyncBegin1(category_group, name, id, arg1_name, \ + arg1_val); + +#define TRACE_EVENT_ASYNC_END1(category_group, name, id, arg1_name, arg1_val) \ + ::fml::tracing::TraceEventAsyncEnd1(category_group, name, id, arg1_name, \ + arg1_val); + +#define TRACE_EVENT_INSTANT0(category_group, name) \ + ::fml::tracing::TraceEventInstant0(category_group, name); + +namespace fml { +namespace tracing { + +using TraceArg = const char*; +using TraceIDArg = int64_t; + +void TraceEvent0(TraceArg category_group, TraceArg name); + +void TraceEvent1(TraceArg category_group, + TraceArg name, + TraceArg arg1_name, + TraceArg arg1_val); + +void TraceEvent2(TraceArg category_group, + TraceArg name, + TraceArg arg1_name, + TraceArg arg1_val, + TraceArg arg2_name, + TraceArg arg2_val); + +void TraceEventEnd(TraceArg name); + +void TraceEventAsyncBegin0(TraceArg category_group, + TraceArg name, + TraceIDArg id); + +void TraceEventAsyncEnd0(TraceArg category_group, TraceArg name, TraceIDArg id); + +void TraceEventAsyncBegin1(TraceArg category_group, + TraceArg name, + TraceIDArg id, + TraceArg arg1_name, + TraceArg arg1_val); + +void TraceEventAsyncEnd1(TraceArg category_group, + TraceArg name, + TraceIDArg id, + TraceArg arg1_name, + TraceArg arg1_val); + +void TraceEventInstant0(TraceArg category_group, TraceArg name); + +class ScopedInstantEnd { + public: + ScopedInstantEnd(std::string str) : label_(std::move(str)) {} + + ~ScopedInstantEnd() { TraceEventEnd(label_.c_str()); } + + private: + const std::string label_; + + FTL_DISALLOW_COPY_AND_ASSIGN(ScopedInstantEnd); +}; + +} // namespace tracing +} // namespace fml + +#endif // FLUTTER_FML_TRACE_EVENT_H_ diff --git a/travis/licenses_golden/licenses_flutter b/travis/licenses_golden/licenses_flutter index bfdcece5b15db0138648ea2e128723a9e047c266..a03484eba58f3dc9119f2ccff7391892ea282631 100644 --- a/travis/licenses_golden/licenses_flutter +++ b/travis/licenses_golden/licenses_flutter @@ -1906,8 +1906,29 @@ FILE: ../../../flutter/content_handler/vulkan_rasterizer.cc FILE: ../../../flutter/content_handler/vulkan_rasterizer.h FILE: ../../../flutter/flow/layers/physical_model_layer.cc FILE: ../../../flutter/flow/layers/physical_model_layer.h +FILE: ../../../flutter/fml/icu_util.cc +FILE: ../../../flutter/fml/icu_util.h +FILE: ../../../flutter/fml/mapping.cc +FILE: ../../../flutter/fml/mapping.h +FILE: ../../../flutter/fml/message_loop.cc +FILE: ../../../flutter/fml/message_loop.h +FILE: ../../../flutter/fml/message_loop_impl.cc +FILE: ../../../flutter/fml/message_loop_impl.h +FILE: ../../../flutter/fml/message_loop_unittests.cc FILE: ../../../flutter/fml/platform/darwin/cf_utils.cc FILE: ../../../flutter/fml/platform/darwin/cf_utils.h +FILE: ../../../flutter/fml/task_runner.cc +FILE: ../../../flutter/fml/task_runner.h +FILE: ../../../flutter/fml/thread.cc +FILE: ../../../flutter/fml/thread.h +FILE: ../../../flutter/fml/thread_checker.cc +FILE: ../../../flutter/fml/thread_checker.h +FILE: ../../../flutter/fml/thread_checker_unittests.cc +FILE: ../../../flutter/fml/thread_local.h +FILE: ../../../flutter/fml/thread_local_unittests.cc +FILE: ../../../flutter/fml/thread_unittests.cc +FILE: ../../../flutter/fml/trace_event.cc +FILE: ../../../flutter/fml/trace_event.h FILE: ../../../flutter/shell/gpu/gpu_surface_software.cc FILE: ../../../flutter/shell/gpu/gpu_surface_software.h FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java