From a396e6af3cd16530202f6cbecbd45c7a3f6ac893 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Apr 2014 22:34:42 -0500 Subject: hw alarms --- src/wakeup-timer-uha.cpp | 168 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 src/wakeup-timer-uha.cpp (limited to 'src/wakeup-timer-uha.cpp') diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp new file mode 100644 index 0000000..dda109f --- /dev/null +++ b/src/wakeup-timer-uha.cpp @@ -0,0 +1,168 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +#include + +#include + +#include // struct timespec +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class UhaWakeupTimer::Impl +{ + +public: + + Impl(const std::shared_ptr& clock): + m_clock(clock), + m_hardware_alarm(u_hardware_alarm_create()) + { + // fire up a worker thread that initially just sleeps + set_wakeup_time_to_the_distant_future(); + m_thread = std::move(std::thread([&](){threadfunc();})); + } + + ~Impl() + { + // tell the worker thread to wake up and exit + m_yielding = true; + set_wakeup_time(m_clock->localtime().add_full(0,0,0,0,0,0.1)); + + // wait for it to happen + if (m_thread.joinable()) + m_thread.join(); + + g_idle_remove_by_data(this); + + u_hardware_alarm_unref(m_hardware_alarm); + } + + void set_wakeup_time(const DateTime& d) + { + g_message("%s %s", G_STRLOC, G_STRFUNC); + std::lock_guard lg(m_mutex); + + g_message("%s setting hardware wakeup time to %s", G_STRFUNC, d.format("%F %T").c_str()); + + const auto diff_usec = d - m_clock->localtime(); + struct timespec ts; + ts.tv_sec = diff_usec / G_USEC_PER_SEC; + ts.tv_nsec = (diff_usec % G_USEC_PER_SEC) * 1000; + g_message("%s setting hardware alarm to kick %zu seconds from now", G_STRFUNC, (size_t)ts.tv_sec); + u_hardware_alarm_set_relative_to_with_behavior(m_hardware_alarm, + U_HARDWARE_ALARM_TIME_REFERENCE_NOW, + U_HARDWARE_ALARM_SLEEP_BEHAVIOR_WAKEUP_DEVICE, + &ts); + } + + core::Signal<>& timeout() { return m_timeout; } + +private: + + void set_wakeup_time_to_the_distant_future() + { + const auto next_year = m_clock->localtime().add_full(1,0,0,0,0,0); + set_wakeup_time(next_year); + } + + static gboolean kick_idle (gpointer gself) + { + static_cast(gself)->timeout(); + + return G_SOURCE_REMOVE; + } + + void threadfunc() + { + while (!m_yielding) + { + // wait for the next hw alarm + UHardwareAlarmWaitResult wait_result; + auto rc = u_hardware_alarm_wait_for_next_alarm(m_hardware_alarm, &wait_result); + g_return_if_fail (rc == U_STATUS_SUCCESS); + + // set a long wakeup interval for the next iteration of the loop. + // if there's another Appointment queued up by the Planner, + // our timeout() listener will call set_wakeup_time() to set the + // real wakeup interval. + set_wakeup_time_to_the_distant_future(); + + // delegate the kick back to the main thread + g_idle_add (kick_idle, this); + } + } + + core::Signal<> m_timeout; + std::recursive_mutex m_mutex; + bool m_yielding = false; + const std::shared_ptr& m_clock; + UHardwareAlarm m_hardware_alarm = nullptr; + std::thread m_thread; +}; + +/*** +**** +***/ + +UhaWakeupTimer::UhaWakeupTimer(const std::shared_ptr& clock): + p(new Impl(clock)) +{ +} + +UhaWakeupTimer::~UhaWakeupTimer() +{ +} + +bool UhaWakeupTimer::is_supported() +{ + auto foo = u_hardware_alarm_create(); + g_message ("%s hardware alarm %p", G_STRFUNC, foo); + return foo != nullptr; +} + +void UhaWakeupTimer::set_wakeup_time(const DateTime& d) +{ + p->set_wakeup_time(d); +} + +core::Signal<>& UhaWakeupTimer::timeout() +{ + return p->timeout(); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity -- cgit v1.2.3 From 79372164fe92a3022df32ef2c6812445126ac00e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 09:05:35 -0500 Subject: in wakeup-timer-uha, set idle wakeup to 1 day from now rather than 1 year from now. --- src/wakeup-timer-uha.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/wakeup-timer-uha.cpp') diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp index b634f52..0db9fb0 100644 --- a/src/wakeup-timer-uha.cpp +++ b/src/wakeup-timer-uha.cpp @@ -89,8 +89,8 @@ private: void set_wakeup_time_to_the_distant_future() { - const auto next_year = m_clock->localtime().add_full(1,0,0,0,0,0); - set_wakeup_time(next_year); + const auto tomorrow = m_clock->localtime().add_full(0,0,1,0,0,0); + set_wakeup_time(tomorrow); } static gboolean kick_idle (gpointer gself) -- cgit v1.2.3 From ce7397dce272c913d93a6bd3852907bc917a4e6f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 09:06:46 -0500 Subject: in wakeup-timer-uha, sync with platform-api changes and add a safeguard to ensure the service doesn't try to sleep for an event that's already passed. --- src/wakeup-timer-uha.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'src/wakeup-timer-uha.cpp') diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp index 0db9fb0..7e19075 100644 --- a/src/wakeup-timer-uha.cpp +++ b/src/wakeup-timer-uha.cpp @@ -71,16 +71,23 @@ public: g_debug("%s %s", G_STRLOC, G_STRFUNC); std::lock_guard lg(m_mutex); - const auto diff_usec = d - m_clock->localtime(); - struct timespec ts; - ts.tv_sec = diff_usec / G_USEC_PER_SEC; - ts.tv_nsec = (diff_usec % G_USEC_PER_SEC) * 1000; - g_debug("%s setting hardware wakeup time to %s (%zu seconds from now)", - G_STRFUNC, (size_t)ts.tv_sec, d.format("%F %T").c_str()); + const auto wakeup_time = d.to_unix(); + + // simple sanity check: don't try to wait for something that's already passed + const auto now = m_clock->localtime().to_unix(); + g_return_if_fail (wakeup_time >= now); + + struct timespec sleep_interval; + sleep_interval.tv_sec = wakeup_time; + sleep_interval.tv_nsec = 0; + g_debug("%s %s setting hardware wakeup time to %s (%zu seconds from now)", + G_STRLOC, G_STRFUNC, + d.format("%F %T").c_str(), + (size_t)(wakeup_time - now)); u_hardware_alarm_set_relative_to_with_behavior(m_hardware_alarm, - U_HARDWARE_ALARM_TIME_REFERENCE_NOW, + U_HARDWARE_ALARM_TIME_REFERENCE_RTC, U_HARDWARE_ALARM_SLEEP_BEHAVIOR_WAKEUP_DEVICE, - &ts); + &sleep_interval); } core::Signal<>& timeout() { return m_timeout; } @@ -106,6 +113,7 @@ private: { // wait for the next hw alarm UHardwareAlarmWaitResult wait_result; + g_debug ("calling wait_for_next_alarm"); auto rc = u_hardware_alarm_wait_for_next_alarm(m_hardware_alarm, &wait_result); g_return_if_fail (rc == U_STATUS_SUCCESS); -- cgit v1.2.3 From e32110f08d6888e25db5bcedb7fd274aa8caea51 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 10 Jun 2014 11:56:07 -0500 Subject: in UhaWakeupTimer, ensure the timeout signal gets emitted from inside a glib idle callback. --- src/wakeup-timer-uha.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/wakeup-timer-uha.cpp') diff --git a/src/wakeup-timer-uha.cpp b/src/wakeup-timer-uha.cpp index 7e19075..437eda2 100644 --- a/src/wakeup-timer-uha.cpp +++ b/src/wakeup-timer-uha.cpp @@ -102,7 +102,7 @@ private: static gboolean kick_idle (gpointer gself) { - static_cast(gself)->timeout(); + static_cast(gself)->m_timeout(); return G_SOURCE_REMOVE; } -- cgit v1.2.3