// Copyright 2021 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_chrono/system_timer.h" #include #include #include #include "pw_chrono_zephyr/system_clock_constants.h" #include "pw_chrono_zephyr/system_timer_native.h" namespace pw::chrono { namespace { constexpr SystemClock::duration kMinTimerPeriod = SystemClock::duration(1); // Work synchronization objects must be in cache-coherent memory, which excludes // stacks on some architectures. static k_work_sync work_sync; } // namespace void HandleTimerWork(k_work* item) { k_work_delayable* delayable_item = k_work_delayable_from_work(item); backend::NativeSystemTimer* native_type = CONTAINER_OF(delayable_item, backend::ZephyrWorkWrapper, work)->owner; sys_mutex_lock(&native_type->mutex, K_FOREVER); #ifdef CONFIG_TIMEOUT_64BIT native_type->user_callback(native_type->expiry_deadline); #else const SystemClock::duration time_until_deadline = native_type->expiry_deadline - SystemClock::now(); if (time_until_deadline <= SystemClock::duration::zero()) { native_type->user_callback(native_type->expiry_deadline); } else { // We haven't met the deadline yet, reschedule as far out as possible. const SystemClock::duration period = std::min(pw::chrono::zephyr::kMaxTimeout, time_until_deadline); k_work_schedule(&native_type->work_wrapper.work, K_TICKS(period.count())); } #endif // CONFIG_TIMEOUT_64BIT sys_mutex_unlock(&native_type->mutex); } SystemTimer::SystemTimer(ExpiryCallback callback) : native_type_{.work_wrapper = { .work = {}, .owner = nullptr, }, .mutex = {}, .expiry_deadline = SystemClock::time_point(), .user_callback = std::move(callback)} { k_work_init_delayable(&native_type_.work_wrapper.work, HandleTimerWork); sys_mutex_init(&native_type_.mutex); native_type_.work_wrapper.owner = &native_type_; } SystemTimer::~SystemTimer() { k_work_cancel_sync(&native_type_.work_wrapper.work, &work_sync); } void SystemTimer::InvokeAt(SystemClock::time_point timestamp) { sys_mutex_lock(&native_type_.mutex, K_FOREVER); native_type_.expiry_deadline = timestamp; const SystemClock::duration time_until_deadline = timestamp - SystemClock::now(); const SystemClock::duration period = IS_ENABLED(CONFIG_TIMEOUT_64BIT) ? time_until_deadline : std::clamp(kMinTimerPeriod, time_until_deadline, pw::chrono::zephyr::kMaxTimeout); k_work_schedule(&native_type_.work_wrapper.work, K_TICKS(period.count())); sys_mutex_unlock(&native_type_.mutex); } } // namespace pw::chrono