From 99a1dde524d318b44fadc269491476f0f1693c98 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 18 May 2018 16:02:01 -0700 Subject: [PATCH] Import FXL timing utilities into FML. (#5311) --- fml/BUILD.gn | 6 ++ fml/time/time_delta.h | 111 ++++++++++++++++++++++++ fml/time/time_delta_unittest.cc | 23 +++++ fml/time/time_point.cc | 76 ++++++++++++++++ fml/time/time_point.h | 70 +++++++++++++++ fml/time/time_point_unittest.cc | 18 ++++ fml/time/time_unittest.cc | 54 ++++++++++++ travis/licenses_golden/licenses_flutter | 6 ++ 8 files changed, 364 insertions(+) create mode 100644 fml/time/time_delta.h create mode 100644 fml/time/time_delta_unittest.cc create mode 100644 fml/time/time_point.cc create mode 100644 fml/time/time_point.h create mode 100644 fml/time/time_point_unittest.cc create mode 100644 fml/time/time_unittest.cc diff --git a/fml/BUILD.gn b/fml/BUILD.gn index dfbd2e0e83a90..e00cd085aed57 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -39,6 +39,9 @@ source_set("fml") { "thread.cc", "thread.h", "thread_local.h", + "time/time_delta.h", + "time/time_point.cc", + "time/time_point.h", "trace_event.cc", "trace_event.h", "unique_fd.cc", @@ -148,6 +151,9 @@ executable("fml_unittests") { "message_loop_unittests.cc", "thread_local_unittests.cc", "thread_unittests.cc", + "time/time_delta_unittest.cc", + "time/time_point_unittest.cc", + "time/time_unittest.cc", ] deps = [ diff --git a/fml/time/time_delta.h b/fml/time/time_delta.h new file mode 100644 index 0000000000000..1b7caa281084f --- /dev/null +++ b/fml/time/time_delta.h @@ -0,0 +1,111 @@ +// Copyright 2016 The Fuchsia 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_TIME_TIME_DELTA_H_ +#define FLUTTER_FML_TIME_TIME_DELTA_H_ + +#include +#include + +#include +#include + +namespace fml { + +// A TimeDelta represents the difference between two time points. +class TimeDelta { + public: + constexpr TimeDelta() = default; + + static constexpr TimeDelta Zero() { return TimeDelta(); } + static constexpr TimeDelta Min() { + return TimeDelta(std::numeric_limits::min()); + } + static constexpr TimeDelta Max() { + return TimeDelta(std::numeric_limits::max()); + } + static constexpr TimeDelta FromNanoseconds(int64_t nanos) { + return TimeDelta(nanos); + } + static constexpr TimeDelta FromMicroseconds(int64_t micros) { + return FromNanoseconds(micros * 1000); + } + static constexpr TimeDelta FromMilliseconds(int64_t millis) { + return FromMicroseconds(millis * 1000); + } + static constexpr TimeDelta FromSeconds(int64_t seconds) { + return FromMilliseconds(seconds * 1000); + } + + static constexpr TimeDelta FromSecondsF(double seconds) { + return FromNanoseconds(seconds * (1000.0 * 1000.0 * 1000.0)); + } + + constexpr int64_t ToNanoseconds() const { return delta_; } + constexpr int64_t ToMicroseconds() const { return ToNanoseconds() / 1000; } + constexpr int64_t ToMilliseconds() const { return ToMicroseconds() / 1000; } + constexpr int64_t ToSeconds() const { return ToMilliseconds() / 1000; } + + constexpr double ToNanosecondsF() const { return delta_; } + constexpr double ToMicrosecondsF() const { return delta_ / 1000.0; } + constexpr double ToMillisecondsF() const { + return delta_ / (1000.0 * 1000.0); + } + constexpr double ToSecondsF() const { + return delta_ / (1000.0 * 1000.0 * 1000.0); + } + + constexpr TimeDelta operator-(TimeDelta other) const { + return TimeDelta::FromNanoseconds(delta_ - other.delta_); + } + + constexpr TimeDelta operator+(TimeDelta other) const { + return TimeDelta::FromNanoseconds(delta_ + other.delta_); + } + + constexpr TimeDelta operator/(int64_t divisor) const { + return TimeDelta::FromNanoseconds(delta_ / divisor); + } + + constexpr int64_t operator/(TimeDelta other) const { + return delta_ / other.delta_; + } + + constexpr TimeDelta operator*(int64_t multiplier) const { + return TimeDelta::FromNanoseconds(delta_ * multiplier); + } + + constexpr TimeDelta operator%(TimeDelta other) const { + return TimeDelta::FromNanoseconds(delta_ % other.delta_); + } + + bool operator==(TimeDelta other) const { return delta_ == other.delta_; } + bool operator!=(TimeDelta other) const { return delta_ != other.delta_; } + bool operator<(TimeDelta other) const { return delta_ < other.delta_; } + bool operator<=(TimeDelta other) const { return delta_ <= other.delta_; } + bool operator>(TimeDelta other) const { return delta_ > other.delta_; } + bool operator>=(TimeDelta other) const { return delta_ >= other.delta_; } + + static constexpr TimeDelta FromTimespec(struct timespec ts) { + return TimeDelta::FromSeconds(ts.tv_sec) + + TimeDelta::FromNanoseconds(ts.tv_nsec); + } + struct timespec ToTimespec() { + struct timespec ts; + constexpr int64_t kNanosecondsPerSecond = 1000000000ll; + ts.tv_sec = static_cast(ToSeconds()); + ts.tv_nsec = delta_ % kNanosecondsPerSecond; + return ts; + } + + private: + // Private, use one of the FromFoo() types + explicit constexpr TimeDelta(int64_t delta) : delta_(delta) {} + + int64_t delta_ = 0; +}; + +} // namespace fml + +#endif // FLUTTER_FML_TIME_TIME_DELTA_H_ diff --git a/fml/time/time_delta_unittest.cc b/fml/time/time_delta_unittest.cc new file mode 100644 index 0000000000000..992b4f4a8b888 --- /dev/null +++ b/fml/time/time_delta_unittest.cc @@ -0,0 +1,23 @@ +// Copyright 2017 The Fuchsia 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/time/time_delta.h" + +#include "gtest/gtest.h" + +namespace fml { +namespace { + +TEST(TimeDelta, Control) { + EXPECT_LT(TimeDelta::Min(), TimeDelta::Zero()); + EXPECT_GT(TimeDelta::Max(), TimeDelta::Zero()); + + EXPECT_GT(TimeDelta::Zero(), TimeDelta::FromMilliseconds(-100)); + EXPECT_LT(TimeDelta::Zero(), TimeDelta::FromMilliseconds(100)); + + EXPECT_EQ(TimeDelta::FromMilliseconds(1000), TimeDelta::FromSeconds(1)); +} + +} // namespace +} // namespace fml diff --git a/fml/time/time_point.cc b/fml/time/time_point.cc new file mode 100644 index 0000000000000..36ca1582372df --- /dev/null +++ b/fml/time/time_point.cc @@ -0,0 +1,76 @@ +// Copyright 2016 The Fuchsia 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/time/time_point.h" + +#include "flutter/fml/build_config.h" + +#if defined(OS_MACOSX) || defined(OS_IOS) +#include +#include +#elif defined(OS_FUCHSIA) +#include +#elif defined(OS_WIN) +#include +#else +#include +#endif // defined(OS_MACOSX) || defined(OS_IOS) + +#include "flutter/fml/logging.h" + +namespace fml { + +// Mac OS X/iOS don't have a (useful) |clock_gettime()|. +// Note: Chromium's |base::TimeTicks::Now()| uses boot time (obtained via +// |sysctl()| with |CTL_KERN|/|KERN_BOOTTIME|). For our current purposes, +// monotonic time (which pauses during sleeps) is sufficient. TODO(vtl): If/when +// we use this for other purposes, maybe we should use boot time (maybe also on +// POSIX). +#if defined(OS_MACOSX) || defined(OS_IOS) + +mach_timebase_info_data_t GetMachTimebaseInfo() { + mach_timebase_info_data_t timebase_info = {}; + kern_return_t error = mach_timebase_info(&timebase_info); + FML_DCHECK(error == KERN_SUCCESS); + return timebase_info; +} + +// static +TimePoint TimePoint::Now() { + static mach_timebase_info_data_t timebase_info = GetMachTimebaseInfo(); + return TimePoint(mach_absolute_time() * timebase_info.numer / + timebase_info.denom); +} + +#elif defined(OS_FUCHSIA) + +// static +TimePoint TimePoint::Now() { + return TimePoint(zx_clock_get(ZX_CLOCK_MONOTONIC)); +} + +#elif defined(OS_WIN) + +TimePoint TimePoint::Now() { + uint64_t freq = 0; + uint64_t count = 0; + QueryPerformanceFrequency((LARGE_INTEGER*)&freq); + QueryPerformanceCounter((LARGE_INTEGER*)&count); + return TimePoint((count * 1000000000) / freq); +} + +#else + +// static +TimePoint TimePoint::Now() { + struct timespec ts; + int res = clock_gettime(CLOCK_MONOTONIC, &ts); + FML_DCHECK(res == 0); + (void)res; + return TimePoint::FromEpochDelta(TimeDelta::FromTimespec(ts)); +} + +#endif // defined(OS_MACOSX) || defined(OS_IOS) + +} // namespace fml diff --git a/fml/time/time_point.h b/fml/time/time_point.h new file mode 100644 index 0000000000000..500de54302a0b --- /dev/null +++ b/fml/time/time_point.h @@ -0,0 +1,70 @@ +// Copyright 2016 The Fuchsia 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_TIME_TIME_POINT_H_ +#define FLUTTER_FML_TIME_TIME_POINT_H_ + +#include + +#include + +#include "flutter/fml/time/time_delta.h" + +namespace fml { + +// A TimePoint represents a point in time represented as an integer number of +// nanoseconds elapsed since an arbitrary point in the past. +// +// WARNING: This class should not be serialized across reboots, or across +// devices: the reference point is only stable for a given device between +// reboots. +class TimePoint { + public: + // Default TimePoint with internal value 0 (epoch). + constexpr TimePoint() = default; + + static TimePoint Now(); + + static constexpr TimePoint Min() { + return TimePoint(std::numeric_limits::min()); + } + + static constexpr TimePoint Max() { + return TimePoint(std::numeric_limits::max()); + } + + static constexpr TimePoint FromEpochDelta(TimeDelta ticks) { + return TimePoint(ticks.ToNanoseconds()); + } + + TimeDelta ToEpochDelta() const { return TimeDelta::FromNanoseconds(ticks_); } + + // Compute the difference between two time points. + TimeDelta operator-(TimePoint other) const { + return TimeDelta::FromNanoseconds(ticks_ - other.ticks_); + } + + TimePoint operator+(TimeDelta duration) const { + return TimePoint(ticks_ + duration.ToNanoseconds()); + } + TimePoint operator-(TimeDelta duration) const { + return TimePoint(ticks_ - duration.ToNanoseconds()); + } + + bool operator==(TimePoint other) const { return ticks_ == other.ticks_; } + bool operator!=(TimePoint other) const { return ticks_ != other.ticks_; } + bool operator<(TimePoint other) const { return ticks_ < other.ticks_; } + bool operator<=(TimePoint other) const { return ticks_ <= other.ticks_; } + bool operator>(TimePoint other) const { return ticks_ > other.ticks_; } + bool operator>=(TimePoint other) const { return ticks_ >= other.ticks_; } + + private: + explicit constexpr TimePoint(int64_t ticks) : ticks_(ticks) {} + + int64_t ticks_ = 0; +}; + +} // namespace fml + +#endif // FLUTTER_FML_TIME_TIME_POINT_H_ diff --git a/fml/time/time_point_unittest.cc b/fml/time/time_point_unittest.cc new file mode 100644 index 0000000000000..2de07b0582442 --- /dev/null +++ b/fml/time/time_point_unittest.cc @@ -0,0 +1,18 @@ +// Copyright 2017 The Fuchsia 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/time/time_point.h" + +#include "gtest/gtest.h" + +namespace fml { +namespace { + +TEST(TimePoint, Control) { + EXPECT_LT(TimePoint::Min(), TimePoint::Now()); + EXPECT_GT(TimePoint::Max(), TimePoint::Now()); +} + +} // namespace +} // namespace fml diff --git a/fml/time/time_unittest.cc b/fml/time/time_unittest.cc new file mode 100644 index 0000000000000..eab352d947773 --- /dev/null +++ b/fml/time/time_unittest.cc @@ -0,0 +1,54 @@ +// Copyright 2016 The Fuchsia 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/time/time_delta.h" +#include "flutter/fml/time/time_point.h" +#include "gtest/gtest.h" + +namespace fml { +namespace { + +TEST(Time, Now) { + auto start = TimePoint::Now(); + for (int i = 0; i < 3; ++i) { + auto now = TimePoint::Now(); + EXPECT_GE(now, start); + std::this_thread::yield(); + } +} + +TEST(Time, IntConversions) { + // Integer conversions should all truncate, not round. + TimeDelta delta = TimeDelta::FromNanoseconds(102304506708ll); + EXPECT_EQ(102304506708ll, delta.ToNanoseconds()); + EXPECT_EQ(102304506ll, delta.ToMicroseconds()); + EXPECT_EQ(102304ll, delta.ToMilliseconds()); + EXPECT_EQ(102ll, delta.ToSeconds()); +} + +TEST(Time, FloatConversions) { + // Float conversions should remain close to the original value. + TimeDelta delta = TimeDelta::FromNanoseconds(102304506708ll); + EXPECT_FLOAT_EQ(102304506708.0, delta.ToNanosecondsF()); + EXPECT_FLOAT_EQ(102304506.708, delta.ToMicrosecondsF()); + EXPECT_FLOAT_EQ(102304.506708, delta.ToMillisecondsF()); + EXPECT_FLOAT_EQ(102.304506708, delta.ToSecondsF()); +} + +TEST(Time, TimespecConversions) { + struct timespec ts; + ts.tv_sec = 5; + ts.tv_nsec = 7; + TimeDelta from_timespec = TimeDelta::FromTimespec(ts); + EXPECT_EQ(5, from_timespec.ToSeconds()); + EXPECT_EQ(5 * 1000000000ll + 7, from_timespec.ToNanoseconds()); + struct timespec to_timespec = from_timespec.ToTimespec(); + EXPECT_EQ(ts.tv_sec, to_timespec.tv_sec); + EXPECT_EQ(ts.tv_nsec, to_timespec.tv_nsec); +} + +} // namespace +} // namespace fml diff --git a/travis/licenses_golden/licenses_flutter b/travis/licenses_golden/licenses_flutter index 2f1a14a290393..09bfe5b0eb0f1 100644 --- a/travis/licenses_golden/licenses_flutter +++ b/travis/licenses_golden/licenses_flutter @@ -660,6 +660,8 @@ LIBRARY: engine ORIGIN: ../../../garnet/LICENSE TYPE: LicenseType.bsd FILE: ../../../flutter/fml/export.h +FILE: ../../../flutter/fml/time/time_delta_unittest.cc +FILE: ../../../flutter/fml/time/time_point_unittest.cc ---------------------------------------------------------------------------------------------------- Copyright 2017 The Fuchsia Authors. All rights reserved. @@ -868,6 +870,10 @@ FILE: ../../../flutter/fml/memory/weak_ptr.h FILE: ../../../flutter/fml/memory/weak_ptr_internal.cc FILE: ../../../flutter/fml/memory/weak_ptr_internal.h FILE: ../../../flutter/fml/memory/weak_ptr_unittest.cc +FILE: ../../../flutter/fml/time/time_delta.h +FILE: ../../../flutter/fml/time/time_point.cc +FILE: ../../../flutter/fml/time/time_point.h +FILE: ../../../flutter/fml/time/time_unittest.cc ---------------------------------------------------------------------------------------------------- Copyright 2016 The Fuchsia Authors. All rights reserved.