xref: /aosp_15_r20/external/pigweed/pw_chrono_stl/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 <mutex>
18 #include <thread>
19 
20 #include "pw_chrono_stl/system_timer_native.h"
21 
22 namespace pw::chrono::backend {
23 namespace internal {
24 
try_acquire()25 bool NoDepsTimedThreadNotification::try_acquire() {
26   std::unique_lock lock(lock_);
27   bool was_set = is_set_;
28   is_set_ = false;
29   return was_set;
30 }
31 
try_acquire_until(SystemClock::time_point deadline)32 bool NoDepsTimedThreadNotification::try_acquire_until(
33     SystemClock::time_point deadline) {
34   std::unique_lock lock(lock_);
35   if (cv_.wait_until(lock, deadline, [&] { return is_set_; })) {
36     is_set_ = false;
37     return true;
38   }
39   return false;
40 }
41 
notify()42 void NoDepsTimedThreadNotification::notify() {
43   {
44     std::unique_lock lock(lock_);
45     is_set_ = true;
46   }
47   cv_.notify_one();
48 }
49 
50 }  // namespace internal
51 
NativeSystemTimer(internal::ExpiryFn && callback)52 NativeSystemTimer::NativeSystemTimer(internal::ExpiryFn&& callback)
53     : timer_state_(
54           std::make_shared<internal::TimerState>(std::move(callback))) {
55   std::thread thread([state = timer_state_]() {
56     while (true) {
57       SystemClock::time_point sleep_until;
58       {
59         std::lock_guard lock(state->lock_);
60         if (!state->running_) {
61           return;
62         }
63         // Execute the callback while the expiry deadline is in the past.
64         // This avoids an unnecessary unlock, sleep attempt, and relock in
65         // the case that the intervals are short or the deadlines are in
66         // the past.
67         while (state->enabled_ &&
68                state->expiry_deadline_ <= SystemClock::now()) {
69           // Unset the thread notification.
70           state->timer_thread_wakeup_.try_acquire();
71           state->enabled_ = false;
72           state->callback_(state->expiry_deadline_);
73         }
74         if (state->enabled_) {
75           sleep_until = state->expiry_deadline_;
76         } else {
77           sleep_until = SystemClock::time_point::max();
78         }
79       }
80       state->timer_thread_wakeup_.try_acquire_until(sleep_until);
81     }
82   });
83   thread.detach();
84 }
85 
InvokeAt(SystemClock::time_point timestamp)86 void NativeSystemTimer::InvokeAt(SystemClock::time_point timestamp) {
87   {
88     std::lock_guard lock(timer_state_->lock_);
89     timer_state_->enabled_ = true;
90     timer_state_->expiry_deadline_ = timestamp;
91   }
92   timer_state_->timer_thread_wakeup_.notify();
93 }
94 
Cancel()95 void NativeSystemTimer::Cancel() {
96   std::lock_guard lock(timer_state_->lock_);
97   timer_state_->enabled_ = false;
98 }
99 
Kill()100 void NativeSystemTimer::Kill() {
101   {
102     std::lock_guard lock(timer_state_->lock_);
103     timer_state_->enabled_ = false;
104     timer_state_->running_ = false;
105   }
106   timer_state_->timer_thread_wakeup_.notify();
107 }
108 
109 }  // namespace pw::chrono::backend
110