diff options
author | Charles Kerr <charles.kerr@canonical.com> | 2015-02-13 18:31:43 +0000 |
---|---|---|
committer | CI Train Bot <ci-train-bot@canonical.com> | 2015-02-13 18:31:43 +0000 |
commit | 5a19a872289889eac16192bfda1fdc2347dfcb99 (patch) | |
tree | 53d024089e6c3c80d887362661a7bdcff8aa83cc | |
parent | 049938feda3ae229277458356764dc8430335cb3 (diff) | |
parent | b6899cbf72d3f1596fc466b1aa365cea83e55410 (diff) | |
download | ayatana-indicator-datetime-5a19a872289889eac16192bfda1fdc2347dfcb99.tar.gz ayatana-indicator-datetime-5a19a872289889eac16192bfda1fdc2347dfcb99.tar.bz2 ayatana-indicator-datetime-5a19a872289889eac16192bfda1fdc2347dfcb99.zip |
change the WallClock to detect time changes from TFD_TIMER_CANCEL_ON_SET, e.g. when ntp<-->manual is toggled
Approved by: Ted Gould, PS Jenkins bot
-rw-r--r-- | src/clock-live.cpp | 137 | ||||
-rw-r--r-- | tests/manual | 6 |
2 files changed, 95 insertions, 48 deletions
diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 68a8a8b..988d47f 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -20,6 +20,15 @@ #include <datetime/clock.h> #include <datetime/timezone.h> +#include <glib-unix.h> // g_unix_fd_add() + +#include <sys/timerfd.h> +#include <unistd.h> // close() + +#ifndef TFD_TIMER_CANCEL_ON_SET + #define TFD_TIMER_CANCEL_ON_SET (1 << 1) +#endif + namespace unity { namespace indicator { namespace datetime { @@ -28,33 +37,6 @@ namespace datetime { **** ***/ -namespace -{ - -void clearTimer(guint& tag) -{ - if (tag) - { - g_source_remove(tag); - tag = 0; - } -} - -guint calculate_milliseconds_until_next_minute(const DateTime& now) -{ - auto next = g_date_time_add_minutes(now.get(), 1); - auto start_of_next = g_date_time_add_seconds (next, -g_date_time_get_seconds(next)); - const auto interval_usec = g_date_time_difference(start_of_next, now.get()); - const guint interval_msec = (interval_usec + 999) / 1000; - g_date_time_unref(start_of_next); - g_date_time_unref(next); - g_assert (interval_msec <= 60000); - return interval_msec; -} - -} // unnamed namespace - - class LiveClock::Impl { public: @@ -70,12 +52,31 @@ public: setter(m_timezone->timezone.get()); } - restart_minute_timer(); + m_timerfd = timerfd_create(CLOCK_REALTIME, 0); + if (m_timerfd == -1) + { + g_warning("unable to create realtime timer: %s", g_strerror(errno)); + } + else + { + reset_timer(); + + m_timerfd_tag = g_unix_fd_add(m_timerfd, + (GIOCondition)(G_IO_IN|G_IO_HUP|G_IO_ERR), + on_timerfd_cond, + this); + } + + refresh(); } ~Impl() { - clearTimer(m_timer); + if (m_timerfd_tag != 0) + g_source_remove(m_timerfd_tag); + + if (m_timerfd != -1) + close(m_timerfd); g_clear_pointer(&m_gtimezone, g_time_zone_unref); } @@ -92,6 +93,61 @@ public: private: + void reset_timer() + { + struct itimerspec timerval; + // set args to fire at the beginning of the next minute... + int flags = TFD_TIMER_ABSTIME; + auto now = g_date_time_new_now(m_gtimezone); + auto next = g_date_time_add_minutes(now, 1); + auto start_of_next = g_date_time_add_seconds(next, -g_date_time_get_seconds(next)); + timerval.it_value.tv_sec = g_date_time_to_unix(start_of_next); + timerval.it_value.tv_nsec = 0; + g_date_time_unref(start_of_next); + g_date_time_unref(next); + g_date_time_unref(now); + // ...and also to fire at the beginning of every subsequent minute... + timerval.it_interval.tv_sec = 60; + timerval.it_interval.tv_nsec = 0; + // ...and also to fire if someone changes the time + // manually (eg toggling from manual<->ntp) + flags |= TFD_TIMER_CANCEL_ON_SET; + + if (timerfd_settime(m_timerfd, flags, &timerval, NULL) == -1) + g_error("timerfd_settime failed: %s", g_strerror(errno)); + } + + static gboolean on_timerfd_cond (gint fd, GIOCondition cond, gpointer gself) + { + auto self = static_cast<Impl*>(gself); + + int n_bytes = 0; + uint64_t n_interrupts = 0; + if (cond & G_IO_IN) + n_bytes = read(fd, &n_interrupts, sizeof(uint64_t)); + + if ((n_interrupts==0) || (n_bytes!=sizeof(uint64_t))) + { + auto now = g_date_time_new_now(self->m_gtimezone); + auto now_str = g_date_time_format(now, "%F %T"); + g_message("%s triggered at %s.%06d by GIOCondition %d, read %zd bytes, found %zu interrupts", + G_STRFUNC, now_str, g_date_time_get_microsecond(now), + (int)cond, n_bytes, n_interrupts); + g_free(now_str); + g_date_time_unref(now); + + // reset the timer in case someone changed the system clock + self->reset_timer(); + } + + self->refresh(); + return G_SOURCE_CONTINUE; + } + + /*** + **** + ***/ + void setTimezone(const std::string& str) { g_clear_pointer(&m_gtimezone, g_time_zone_unref); @@ -103,33 +159,17 @@ private: **** ***/ - void restart_minute_timer() + void refresh() { - clearTimer(m_timer); + const auto now = localtime(); // maybe emit change signals - const auto now = localtime(); if (!DateTime::is_same_minute(m_prev_datetime, now)) m_owner.minute_changed(); if (!DateTime::is_same_day(m_prev_datetime, now)) m_owner.date_changed(); - // queue up a timer to fire at the next minute m_prev_datetime = now; - auto interval_msec = calculate_milliseconds_until_next_minute(now); - interval_msec += 50; // add a small margin to ensure the callback - // fires /after/ next is reached - m_timer = g_timeout_add_full(G_PRIORITY_HIGH, - interval_msec, - on_minute_timer_reached, - this, - nullptr); - } - - static gboolean on_minute_timer_reached(gpointer gself) - { - static_cast<LiveClock::Impl*>(gself)->restart_minute_timer(); - return G_SOURCE_REMOVE; } protected: @@ -139,7 +179,8 @@ protected: std::shared_ptr<const Timezone> m_timezone; DateTime m_prev_datetime; - unsigned int m_timer = 0; + int m_timerfd = -1; + guint m_timerfd_tag = 0; }; LiveClock::LiveClock(const std::shared_ptr<const Timezone>& timezone_): diff --git a/tests/manual b/tests/manual index 007f967..95e5401 100644 --- a/tests/manual +++ b/tests/manual @@ -137,6 +137,12 @@ Test-case indicator-datetime/silent-mode <dd>The calendar event notification should play a sound.</dd> </dl> +Test-case indicator-datetime/manual-time +<dl> + <dt>In System Settings > Time & Date, manually change the time to an arbitrary time.</dt> + <dd>The indicator's timestamp should change right after the time manual time is set.</dd> +</dl> + <strong> If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result. If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result</strong>. |