|
rpm-build |
18b009 |
/* -*- mode: C; c-file-style: "linux"; indent-tabs-mode: t -*-
|
|
rpm-build |
18b009 |
* gdatetime-source.c - copy&paste from https://bugzilla.gnome.org/show_bug.cgi?id=655129
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* Copyright (C) 2011 Red Hat, Inc.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* This program is free software; you can redistribute it and/or
|
|
rpm-build |
18b009 |
* modify it under the terms of the GNU Library General Public License
|
|
rpm-build |
18b009 |
* as published by the Free Software Foundation; either version 2 of
|
|
rpm-build |
18b009 |
* the License, or (at your option) any later version.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* This program is distributed in the hope that it will be useful, but
|
|
rpm-build |
18b009 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
rpm-build |
18b009 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
rpm-build |
18b009 |
* Library General Public License for more details.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* You should have received a copy of the GNU Library General Public
|
|
rpm-build |
18b009 |
* License along with this program; if not, write to the Free Software
|
|
rpm-build |
18b009 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
rpm-build |
18b009 |
* 02110-1301, USA.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* Author: Colin Walters <walters@verbum.org>
|
|
rpm-build |
18b009 |
*/
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
#include "config.h"
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
#define GNOME_DESKTOP_USE_UNSTABLE_API
|
|
rpm-build |
18b009 |
#include "gnome-datetime-source.h"
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
#ifdef HAVE_TIMERFD
|
|
rpm-build |
18b009 |
#include <sys/timerfd.h>
|
|
rpm-build |
18b009 |
#include <unistd.h>
|
|
rpm-build |
18b009 |
#include <string.h>
|
|
rpm-build |
18b009 |
#endif
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
typedef struct _GDateTimeSource GDateTimeSource;
|
|
rpm-build |
18b009 |
struct _GDateTimeSource
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
GSource source;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
gint64 real_expiration;
|
|
rpm-build |
18b009 |
gint64 wakeup_expiration;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
gboolean cancel_on_set : 1;
|
|
rpm-build |
18b009 |
gboolean initially_expired : 1;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
GPollFD pollfd;
|
|
rpm-build |
18b009 |
};
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
static inline void
|
|
rpm-build |
18b009 |
g_datetime_source_reschedule (GDateTimeSource *datetime_source,
|
|
rpm-build |
18b009 |
gint64 from_monotonic)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
datetime_source->wakeup_expiration = from_monotonic + G_TIME_SPAN_SECOND;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
static gboolean
|
|
rpm-build |
18b009 |
g_datetime_source_is_expired (GDateTimeSource *datetime_source)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
gint64 real_now;
|
|
rpm-build |
18b009 |
gint64 monotonic_now;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
real_now = g_get_real_time ();
|
|
rpm-build |
18b009 |
monotonic_now = g_source_get_time ((GSource*)datetime_source);
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
if (datetime_source->initially_expired)
|
|
rpm-build |
18b009 |
return TRUE;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
if (datetime_source->real_expiration <= real_now)
|
|
rpm-build |
18b009 |
return TRUE;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
/* We can't really detect without system support when things
|
|
rpm-build |
18b009 |
* change; so just trigger every second (i.e. our wakeup
|
|
rpm-build |
18b009 |
* expiration)
|
|
rpm-build |
18b009 |
*/
|
|
rpm-build |
18b009 |
if (datetime_source->cancel_on_set && monotonic_now >= datetime_source->wakeup_expiration)
|
|
rpm-build |
18b009 |
return TRUE;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
return FALSE;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
/* In prepare, we're just checking the monotonic time against
|
|
rpm-build |
18b009 |
* our projected wakeup.
|
|
rpm-build |
18b009 |
*/
|
|
rpm-build |
18b009 |
static gboolean
|
|
rpm-build |
18b009 |
g_datetime_source_prepare (GSource *source,
|
|
rpm-build |
18b009 |
gint *timeout)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
GDateTimeSource *datetime_source = (GDateTimeSource*)source;
|
|
rpm-build |
18b009 |
gint64 monotonic_now;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
#ifdef HAVE_TIMERFD
|
|
rpm-build |
18b009 |
if (datetime_source->pollfd.fd != -1) {
|
|
rpm-build |
18b009 |
*timeout = -1;
|
|
rpm-build |
18b009 |
return datetime_source->initially_expired; /* Should be TRUE at most one time, FALSE forever after */
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
#endif
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
monotonic_now = g_source_get_time (source);
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
if (monotonic_now < datetime_source->wakeup_expiration) {
|
|
rpm-build |
18b009 |
/* Round up to ensure that we don't try again too early */
|
|
rpm-build |
18b009 |
*timeout = (datetime_source->wakeup_expiration - monotonic_now + 999) / 1000;
|
|
rpm-build |
18b009 |
return FALSE;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
*timeout = 0;
|
|
rpm-build |
18b009 |
return g_datetime_source_is_expired (datetime_source);
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
/* In check, we're looking at the wall clock.
|
|
rpm-build |
18b009 |
*/
|
|
rpm-build |
18b009 |
static gboolean
|
|
rpm-build |
18b009 |
g_datetime_source_check (GSource *source)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
GDateTimeSource *datetime_source = (GDateTimeSource*)source;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
#ifdef HAVE_TIMERFD
|
|
rpm-build |
18b009 |
if (datetime_source->pollfd.fd != -1)
|
|
rpm-build |
18b009 |
return datetime_source->pollfd.revents != 0;
|
|
rpm-build |
18b009 |
#endif
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
if (g_datetime_source_is_expired (datetime_source))
|
|
rpm-build |
18b009 |
return TRUE;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
g_datetime_source_reschedule (datetime_source, g_source_get_time (source));
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
return FALSE;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
static gboolean
|
|
rpm-build |
18b009 |
g_datetime_source_dispatch (GSource *source,
|
|
rpm-build |
18b009 |
GSourceFunc callback,
|
|
rpm-build |
18b009 |
gpointer user_data)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
GDateTimeSource *datetime_source = (GDateTimeSource*)source;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
datetime_source->initially_expired = FALSE;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
if (!callback) {
|
|
rpm-build |
18b009 |
g_warning ("Timeout source dispatched without callback\n"
|
|
rpm-build |
18b009 |
"You must call g_source_set_callback().");
|
|
rpm-build |
18b009 |
return FALSE;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
(callback) (user_data);
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
/* Always false as this source is documented to run once */
|
|
rpm-build |
18b009 |
return FALSE;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
static void
|
|
rpm-build |
18b009 |
g_datetime_source_finalize (GSource *source)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
#ifdef HAVE_TIMERFD
|
|
rpm-build |
18b009 |
GDateTimeSource *datetime_source = (GDateTimeSource*)source;
|
|
rpm-build |
18b009 |
if (datetime_source->pollfd.fd != -1)
|
|
rpm-build |
18b009 |
close (datetime_source->pollfd.fd);
|
|
rpm-build |
18b009 |
#endif
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
static GSourceFuncs g_datetime_source_funcs = {
|
|
rpm-build |
18b009 |
g_datetime_source_prepare,
|
|
rpm-build |
18b009 |
g_datetime_source_check,
|
|
rpm-build |
18b009 |
g_datetime_source_dispatch,
|
|
rpm-build |
18b009 |
g_datetime_source_finalize
|
|
rpm-build |
18b009 |
};
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
#ifdef HAVE_TIMERFD
|
|
rpm-build |
18b009 |
static gboolean
|
|
rpm-build |
18b009 |
g_datetime_source_init_timerfd (GDateTimeSource *datetime_source,
|
|
rpm-build |
18b009 |
gint64 expected_now_seconds,
|
|
rpm-build |
18b009 |
gint64 unix_seconds)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
struct itimerspec its;
|
|
rpm-build |
18b009 |
int settime_flags;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
datetime_source->pollfd.fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC);
|
|
rpm-build |
18b009 |
if (datetime_source->pollfd.fd == -1)
|
|
rpm-build |
18b009 |
return FALSE;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
memset (&its, 0, sizeof (its));
|
|
rpm-build |
18b009 |
its.it_value.tv_sec = (time_t) unix_seconds;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
/* http://article.gmane.org/gmane.linux.kernel/1132138 */
|
|
rpm-build |
18b009 |
#ifndef TFD_TIMER_CANCEL_ON_SET
|
|
rpm-build |
18b009 |
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
|
|
rpm-build |
18b009 |
#endif
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
settime_flags = TFD_TIMER_ABSTIME;
|
|
rpm-build |
18b009 |
if (datetime_source->cancel_on_set)
|
|
rpm-build |
18b009 |
settime_flags |= TFD_TIMER_CANCEL_ON_SET;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
if (timerfd_settime (datetime_source->pollfd.fd, settime_flags, &its, NULL) < 0) {
|
|
rpm-build |
18b009 |
close (datetime_source->pollfd.fd);
|
|
rpm-build |
18b009 |
datetime_source->pollfd.fd = -1;
|
|
rpm-build |
18b009 |
return FALSE;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
/* Now we need to check that the clock didn't go backwards before we
|
|
rpm-build |
18b009 |
* had the timerfd set up. See
|
|
rpm-build |
18b009 |
* https://bugzilla.gnome.org/show_bug.cgi?id=655129
|
|
rpm-build |
18b009 |
*/
|
|
rpm-build |
18b009 |
clock_gettime (CLOCK_REALTIME, &its.it_value);
|
|
rpm-build |
18b009 |
if (its.it_value.tv_sec < expected_now_seconds)
|
|
rpm-build |
18b009 |
datetime_source->initially_expired = TRUE;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
datetime_source->pollfd.events = G_IO_IN;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
g_source_add_poll ((GSource*) datetime_source, &datetime_source->pollfd);
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
return TRUE;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
#endif
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
/**
|
|
rpm-build |
18b009 |
* _gnome_date_time_source_new:
|
|
rpm-build |
18b009 |
* @now: The expected current time
|
|
rpm-build |
18b009 |
* @expiry: Time to await
|
|
rpm-build |
18b009 |
* @cancel_on_set: Also invoke callback if the system clock changes discontiguously
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* This function is designed for programs that want to schedule an
|
|
rpm-build |
18b009 |
* event based on real (wall clock) time, as returned by
|
|
rpm-build |
18b009 |
* g_get_real_time(). For example, HOUR:MINUTE wall-clock displays
|
|
rpm-build |
18b009 |
* and calendaring software. The callback will be invoked when the
|
|
rpm-build |
18b009 |
* specified wall clock time @expiry is reached. This includes
|
|
rpm-build |
18b009 |
* events such as the system clock being set past the given time.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* Compare versus g_timeout_source_new() which is defined to use
|
|
rpm-build |
18b009 |
* monotonic time as returned by g_get_monotonic_time().
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* The parameter @now is necessary to avoid a race condition in
|
|
rpm-build |
18b009 |
* between getting the current time and calling this function.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* If @cancel_on_set is given, the callback will also be invoked at
|
|
rpm-build |
18b009 |
* most a second after the system clock is changed. This includes
|
|
rpm-build |
18b009 |
* being set backwards or forwards, and system
|
|
rpm-build |
18b009 |
* resume from suspend. Not all operating systems allow detecting all
|
|
rpm-build |
18b009 |
* relevant events efficiently - this function may cause the process
|
|
rpm-build |
18b009 |
* to wake up once a second in those cases.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* A wall clock display should use @cancel_on_set; a calendaring
|
|
rpm-build |
18b009 |
* program shouldn't need to.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* Note that the return value from the associated callback will be
|
|
rpm-build |
18b009 |
* ignored; this is a one time watch.
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* <note><para>This function currently does not detect time zone
|
|
rpm-build |
18b009 |
* changes. On Linux, your program should also monitor the
|
|
rpm-build |
18b009 |
* <literal>/etc/timezone</literal> file using
|
|
rpm-build |
18b009 |
* #GFileMonitor.</para></note>
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* <example id="gdatetime-example-watch"><title>Clock example</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../glib/tests/glib-clock.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* Return value: A newly-constructed #GSource
|
|
rpm-build |
18b009 |
*
|
|
rpm-build |
18b009 |
* Since: 2.30
|
|
rpm-build |
18b009 |
**/
|
|
rpm-build |
18b009 |
GSource *
|
|
rpm-build |
18b009 |
_gnome_datetime_source_new (GDateTime *now,
|
|
rpm-build |
18b009 |
GDateTime *expiry,
|
|
rpm-build |
18b009 |
gboolean cancel_on_set)
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
GDateTimeSource *datetime_source;
|
|
rpm-build |
18b009 |
gint64 unix_seconds;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
unix_seconds = g_date_time_to_unix (expiry);
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
datetime_source = (GDateTimeSource*) g_source_new (&g_datetime_source_funcs, sizeof (GDateTimeSource));
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
datetime_source->cancel_on_set = cancel_on_set;
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
#ifdef HAVE_TIMERFD
|
|
rpm-build |
18b009 |
{
|
|
rpm-build |
18b009 |
gint64 expected_now_seconds = g_date_time_to_unix (now);
|
|
rpm-build |
18b009 |
if (g_datetime_source_init_timerfd (datetime_source, expected_now_seconds, unix_seconds))
|
|
rpm-build |
18b009 |
return (GSource*)datetime_source;
|
|
rpm-build |
18b009 |
/* Fall through to non-timerfd code */
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
#endif
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
datetime_source->real_expiration = unix_seconds * 1000000;
|
|
rpm-build |
18b009 |
g_datetime_source_reschedule (datetime_source, g_get_monotonic_time ());
|
|
rpm-build |
18b009 |
|
|
rpm-build |
18b009 |
return (GSource*)datetime_source;
|
|
rpm-build |
18b009 |
}
|
|
rpm-build |
18b009 |
|