xref: /aosp_15_r20/external/pigweed/pw_chrono_embos/system_timer.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 
15 #include "pw_chrono/system_timer.h"
16 
17 #include <algorithm>
18 #include <mutex>
19 
20 #include "RTOS.h"
21 #include "pw_assert/check.h"
22 #include "pw_chrono_embos/system_clock_constants.h"
23 #include "pw_interrupt/context.h"
24 
25 namespace pw::chrono {
26 namespace {
27 
28 // Instead of adding targeted locks to each instance, simply use the global
29 // recursive critical section lock. Note it has to be recursive because a user
30 // callback may use the Invoke* API which in turn needs to grab the lock.
31 class RecursiveCriticalSectionLock {
32  public:
lock()33   void lock() {
34     OS_IncDI();            // Mask interrupts.
35     OS_SuspendAllTasks();  // Disable task switching.
36   }
37 
unlock()38   void unlock() {
39     OS_ResumeAllSuspendedTasks();  // Restore task switching.
40     OS_DecRI();                    // Restore interrupts.
41   }
42 };
43 RecursiveCriticalSectionLock recursive_global_timer_lock;
44 
HandleTimerCallback(void * void_native_system_timer)45 void HandleTimerCallback(void* void_native_system_timer) {
46   PW_DCHECK(interrupt::InInterruptContext(),
47             "HandleTimerCallback must be invoked from an interrupt");
48   std::lock_guard lock(recursive_global_timer_lock);
49 
50   backend::NativeSystemTimer& native_type =
51       *static_cast<backend::NativeSystemTimer*>(void_native_system_timer);
52   const SystemClock::duration time_until_deadline =
53       native_type.expiry_deadline - SystemClock::now();
54   if (time_until_deadline <= SystemClock::duration::zero()) {
55     // We have met the deadline, execute the user's callback.
56     native_type.user_callback(native_type.expiry_deadline);
57     return;
58   }
59   const SystemClock::duration period =
60       std::min(pw::chrono::embos::kMaxTimeout, time_until_deadline);
61   OS_SetTimerPeriodEx(&native_type.tcb, static_cast<OS_TIME>(period.count()));
62   OS_StartTimerEx(&native_type.tcb);
63 }
64 
65 // embOS requires a timer to have a non-zero period.
66 constexpr SystemClock::duration kMinTimerPeriod = SystemClock::duration(1);
67 constexpr OS_TIME kInvalidPeriod = 0;
68 
69 }  // namespace
70 
SystemTimer(ExpiryCallback && callback)71 SystemTimer::SystemTimer(ExpiryCallback&& callback)
72     : native_type_{.tcb{},
73                    .expiry_deadline = SystemClock::time_point(),
74                    .user_callback = std::move(callback)} {
75   OS_CreateTimerEx(
76       &native_type_.tcb, HandleTimerCallback, kInvalidPeriod, &native_type_);
77 }
78 
~SystemTimer()79 SystemTimer::~SystemTimer() {
80   // Not threadsafe by design.
81   Cancel();
82   OS_DeleteTimerEx(&native_type_.tcb);
83 }
84 
InvokeAt(SystemClock::time_point timestamp)85 void SystemTimer::InvokeAt(SystemClock::time_point timestamp) {
86   std::lock_guard lock(recursive_global_timer_lock);
87 
88   // Ensure the timer has been cancelled first.
89   Cancel();
90 
91   native_type_.expiry_deadline = timestamp;
92   const SystemClock::duration time_until_deadline =
93       timestamp - SystemClock::now();
94 
95   // Schedule the timer as far out as possible. Note that the timeout might be
96   // clamped and it may be rescheduled internally.
97   const SystemClock::duration period = std::clamp(
98       kMinTimerPeriod, time_until_deadline, pw::chrono::embos::kMaxTimeout);
99 
100   OS_SetTimerPeriodEx(&native_type_.tcb, static_cast<OS_TIME>(period.count()));
101   OS_RetriggerTimerEx(&native_type_.tcb);
102 }
103 
104 }  // namespace pw::chrono
105