1 // Copyright 2021 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include "pw_chrono/system_clock.h" 17 #include "pw_chrono_backend/system_timer_native.h" 18 #include "pw_function/function.h" 19 20 namespace pw::chrono { 21 22 /// The `SystemTimer` allows an `ExpiryCallback` be executed at a set time in 23 /// the future. 24 /// 25 /// The base `SystemTimer` only supports a one-shot style timer with a callback. 26 /// A periodic timer can be implemented by rescheduling the timer in the 27 /// callback through `InvokeAt(kDesiredPeriod + expired_deadline)`. 28 /// 29 /// When implementing a periodic layer on top, the user should be mindful of 30 /// handling missed periodic callbacks. They could opt to invoke the callback 31 /// multiple times with the expected expired_deadline values or instead saturate 32 /// and invoke the callback only once with the latest expired_deadline. 33 /// 34 /// The entire API is thread safe, however it is NOT always IRQ safe. 35 class SystemTimer { 36 public: 37 using native_handle_type = backend::NativeSystemTimerHandle; 38 39 /// The `ExpiryCallback` is either invoked from a high priority thread or an 40 /// interrupt. 41 /// 42 /// For a given timer instance, its `ExpiryCallback` will not preempt itself. 43 /// This makes it appear like there is a single executor of a timer instance's 44 /// `ExpiryCallback`. 45 /// 46 /// Ergo ExpiryCallbacks should be treated as if they are executed by an 47 /// interrupt, meaning: 48 /// - Processing inside of the callback should be kept to a minimum. 49 /// - Callbacks should never attempt to block. 50 /// - APIs which are not interrupt safe such as pw::sync::Mutex should not be 51 /// used! 52 using ExpiryCallback = 53 Function<void(SystemClock::time_point expired_deadline)>; 54 55 /// Constructs the SystemTimer based on the user provided 56 /// `pw::Function<void(SystemClock::time_point expired_deadline)>`. Note that 57 /// The `ExpiryCallback` is either invoked from a high priority thread or an 58 /// interrupt. 59 SystemTimer(ExpiryCallback&& callback); 60 61 /// Cancels the timer and blocks if necssary if the callback is already being 62 /// processed. 63 /// 64 /// @b Postcondition: The expiry callback is not in progress and will not be 65 /// called in the future. 66 ~SystemTimer(); 67 68 SystemTimer(const SystemTimer&) = delete; 69 SystemTimer(SystemTimer&&) = delete; 70 SystemTimer& operator=(const SystemTimer&) = delete; 71 SystemTimer& operator=(SystemTimer&&) = delete; 72 73 /// Invokes the expiry callback as soon as possible after at least the 74 /// specified duration. 75 /// 76 /// Scheduling a callback cancels the existing callback (if pending). 77 /// If the callback is already being executed while you reschedule it, it will 78 /// finish callback execution to completion. You are responsible for any 79 /// critical section locks which may be needed for timer coordination. 80 /// 81 /// This is thread safe, it may not be IRQ safe. 82 void InvokeAfter(SystemClock::duration delay); 83 84 /// Invokes the expiry callback as soon as possible starting at the specified 85 /// time_point. 86 /// 87 /// Scheduling a callback cancels the existing callback (if pending). 88 /// If the callback is already being executed while you reschedule it, it will 89 /// finish callback execution to completion. You are responsible for any 90 /// critical section locks which may be needed for timer coordination. 91 /// 92 /// This is thread safe, it may not be IRQ safe. 93 void InvokeAt(SystemClock::time_point timestamp); 94 95 /// Cancels the software timer expiry callback if pending. 96 /// 97 /// Canceling a timer which isn't scheduled does nothing. 98 /// 99 /// If the callback is already being executed while you cancel it, it will 100 /// finish callback execution to completion. You are responsible for any 101 /// synchronization which is needed for thread safety. 102 /// 103 /// This is thread safe, it may not be IRQ safe. 104 void Cancel(); 105 106 native_handle_type native_handle(); 107 108 private: 109 /// This may be a wrapper around a native type with additional members. 110 backend::NativeSystemTimer native_type_; 111 }; 112 113 } // namespace pw::chrono 114 115 #include "pw_chrono_backend/system_timer_inline.h" 116