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()25bool 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)32bool 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()42void 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)52NativeSystemTimer::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)86void 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()95void NativeSystemTimer::Cancel() { 96 std::lock_guard lock(timer_state_->lock_); 97 timer_state_->enabled_ = false; 98 } 99 Kill()100void 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