From b59ed0b70f86c0dc1496c3145fa114bc38965f51 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Tue, 21 Feb 2023 08:00:31 -0800 Subject: [PATCH] Add Apple implementation for timer PiperOrigin-RevId: 511201457 --- .../platform/implementation/apple/platform.mm | 1 - .../platform/implementation/apple/timer.h | 8 ++- .../platform/implementation/apple/timer.mm | 63 ++++++++++++++++++- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/internal/platform/implementation/apple/platform.mm b/internal/platform/implementation/apple/platform.mm index 2e442403..bc038d6a 100644 --- a/internal/platform/implementation/apple/platform.mm +++ b/internal/platform/implementation/apple/platform.mm @@ -224,7 +224,6 @@ absl::StatusOr ImplementationPlatform::SendRequest(const WebRequest return webResponse; } -// TODO(b/261511529): Add implementation. std::unique_ptr ImplementationPlatform::CreateTimer() { return std::make_unique(); } diff --git a/internal/platform/implementation/apple/timer.h b/internal/platform/implementation/apple/timer.h index 068629f5..26090e84 100644 --- a/internal/platform/implementation/apple/timer.h +++ b/internal/platform/implementation/apple/timer.h @@ -15,6 +15,8 @@ #ifndef PLATFORM_IMPL_APPLE_TIMER_H_ #define PLATFORM_IMPL_APPLE_TIMER_H_ +#import + #include #include "internal/platform/implementation/timer.h" @@ -25,7 +27,7 @@ namespace apple { class Timer : public api::Timer { public: Timer() = default; - ~Timer() override = default; + ~Timer() override; bool Create(int delay, int interval, absl::AnyInvocable callback) override; @@ -33,6 +35,10 @@ class Timer : public api::Timer { bool Stop() override; bool FireNow() override; + + private: + absl::AnyInvocable callback_; + dispatch_source_t timer_; }; } // namespace apple diff --git a/internal/platform/implementation/apple/timer.mm b/internal/platform/implementation/apple/timer.mm index 252f063d..7ea292e7 100644 --- a/internal/platform/implementation/apple/timer.mm +++ b/internal/platform/implementation/apple/timer.mm @@ -17,15 +17,72 @@ #include #include "internal/platform/implementation/timer.h" +#import "GoogleToolboxForMac/GTMLogger.h" + +// The leeway parameter is a hint from the application as to the amount of time, in nanoseconds, up +// to which the system can defer the timer to align with other system activity for improved system +// performance or power consumption. For example, an application might perform a periodic task every +// 5 minutes, with a leeway of up to 30 seconds. Note that some latency is to be expected for all +// timers, even when a leeway value of zero is specified. +uint64_t const GNCTimerLeewayInNanoseconds = 0; namespace nearby { namespace apple { -bool Timer::Create(int delay, int interval, absl::AnyInvocable callback) { return true; } +Timer::~Timer() { Stop(); } -bool Timer::Stop() { return true; } +bool Timer::Create(int delay, int interval, absl::AnyInvocable callback) { + if (delay < 0 || interval < 0) { + GTMLoggerError(@"Delay and interval must be positive or zero."); + return false; + } -bool Timer::FireNow() { return true; } + if (timer_ != nil) { + GTMLoggerError(@"Timer has already started."); + return false; + } + + uint64_t delayInNanoseconds = delay * NSEC_PER_MSEC; + // If `interval` is 0, it means we only want the timer to fire once. We map this to + // `DISPATCH_TIME_FOREVER` to have this effect. + uint64_t intervalInNanoseconds = interval == 0 ? DISPATCH_TIME_FOREVER : interval * NSEC_PER_MSEC; + callback_ = std::move(callback); + + timer_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, /*handle=*/0, /*mask=*/0, + dispatch_get_main_queue()); + + dispatch_source_set_event_handler(timer_, ^{ + if (callback_ != nil) { + callback_(); + } + // If our interval is `DISPATCH_TIME_FOREVER`, it means we only want the timer to fire once. + if (intervalInNanoseconds == DISPATCH_TIME_FOREVER) { + Stop(); + } + }); + + dispatch_source_set_timer(timer_, dispatch_time(DISPATCH_TIME_NOW, delayInNanoseconds), + intervalInNanoseconds, GNCTimerLeewayInNanoseconds); + dispatch_resume(timer_); + return true; +} + +bool Timer::Stop() { + if (timer_ != nil) { + dispatch_source_cancel(timer_); + } + timer_ = nil; + callback_ = nil; + return true; +} + +bool Timer::FireNow() { + if (callback_ != nil) { + callback_(); + return true; + } + return false; +} } // namespace apple } // namespace nearby