#include "gatomic.h"
#include "gfileutils.h"
#include "ghash.h"
-#include "giochannel.h"
#include "gmain.h"
#include "gmappedfile.h"
#include "gstrfuncs.h"
#ifndef G_OS_WIN32
#include <sys/time.h>
#include <time.h>
-#ifdef HAVE_TIMERFD
-#include <sys/timerfd.h>
-#endif
#endif /* !G_OS_WIN32 */
/**
return NULL;
}
-typedef struct _GDateTimeSource GDateTimeSource;
-struct _GDateTimeSource
-{
- GSource source;
-
- gint64 real_expiration;
- gint64 wakeup_expiration;
-
- gboolean cancel_on_set;
-
- GPollFD pollfd;
-};
-
-static inline void
-g_datetime_source_reschedule (GDateTimeSource *datetime_source,
- gint64 from_monotonic)
-{
- datetime_source->wakeup_expiration = from_monotonic + G_TIME_SPAN_SECOND;
-}
-
-static gboolean
-g_datetime_source_is_expired (GDateTimeSource *datetime_source)
-{
- gint64 real_now;
-
- real_now = g_get_real_time ();
-
- if (datetime_source->real_expiration <= real_now)
- return TRUE;
-
- /* We can't really detect without system support when things change;
- * so just trigger every second.
- */
- if (datetime_source->cancel_on_set)
- return TRUE;
-
- return FALSE;
-}
-
-/* In prepare, we're just checking the monotonic time against
- * our projected wakeup.
- */
-static gboolean
-g_datetime_source_prepare (GSource *source,
- gint *timeout)
-{
- GDateTimeSource *datetime_source = (GDateTimeSource*)source;
- gint64 monotonic_now;
-
-#ifdef HAVE_TIMERFD
- if (datetime_source->pollfd.fd != -1)
- {
- *timeout = -1;
- return FALSE;
- }
-#endif
-
- monotonic_now = g_source_get_time (source);
-
- if (monotonic_now < datetime_source->wakeup_expiration)
- {
- /* Round up to ensure that we don't try again too early */
- *timeout = (datetime_source->wakeup_expiration - monotonic_now + 999) / 1000;
- return FALSE;
- }
-
- *timeout = 0;
- return g_datetime_source_is_expired (datetime_source);
-}
-
-/* In check, we're looking at the wall clock.
- */
-static gboolean
-g_datetime_source_check (GSource *source)
-{
- GDateTimeSource *datetime_source = (GDateTimeSource*)source;
-
-#ifdef HAVE_TIMERFD
- if (datetime_source->pollfd.fd != -1)
- return datetime_source->pollfd.revents != 0;
-#endif
-
- if (g_datetime_source_is_expired (datetime_source))
- return TRUE;
-
- g_datetime_source_reschedule (datetime_source, g_source_get_time (source));
-
- return FALSE;
-}
-
-static gboolean
-g_datetime_source_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-{
- if (!callback)
- {
- g_warning ("Timeout source dispatched without callback\n"
- "You must call g_source_set_callback().");
- return FALSE;
- }
-
- (callback) (user_data);
-
- /* Always false as this source is documented to run once */
- return FALSE;
-}
-
-static void
-g_datetime_source_finalize (GSource *source)
-{
-#ifdef HAVE_TIMERFD
- GDateTimeSource *datetime_source = (GDateTimeSource*)source;
- if (datetime_source->pollfd.fd != -1)
- close (datetime_source->pollfd.fd);
-#endif
-}
-
-static GSourceFuncs g_datetime_source_funcs = {
- g_datetime_source_prepare,
- g_datetime_source_check,
- g_datetime_source_dispatch,
- g_datetime_source_finalize
-};
-
-#ifdef HAVE_TIMERFD
-static gboolean
-g_datetime_source_init_timerfd (GDateTimeSource *datetime_source,
- gint64 unix_seconds)
-{
- struct itimerspec its;
- int settime_flags;
-
- datetime_source->pollfd.fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC);
- if (datetime_source->pollfd.fd == -1)
- return FALSE;
-
- memset (&its, 0, sizeof (its));
- its.it_value.tv_sec = (time_t) unix_seconds;
-
- /* http://article.gmane.org/gmane.linux.kernel/1132138 */
-#ifndef TFD_TIMER_CANCEL_ON_SET
-#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
-#endif
-
- settime_flags = TFD_TIMER_ABSTIME;
- if (datetime_source->cancel_on_set)
- settime_flags |= TFD_TIMER_CANCEL_ON_SET;
-
- if (timerfd_settime (datetime_source->pollfd.fd, settime_flags, &its, NULL) < 0)
- {
- close (datetime_source->pollfd.fd);
- datetime_source->pollfd.fd = -1;
- return FALSE;
- }
-
- datetime_source->pollfd.events = G_IO_IN;
-
- g_source_add_poll ((GSource*) datetime_source, &datetime_source->pollfd);
-
- return TRUE;
-}
-#endif
-
-/**
- * g_date_time_source_new:
- * @datetime: Time to await
- * @cancel_on_set: Also invoke callback if the system clock changes discontiguously
- *
- * This function is designed for programs that want to schedule an
- * event based on real (wall clock) time, as returned by
- * g_get_real_time(). For example, HOUR:MINUTE wall-clock displays
- * and calendaring software. The callback will be invoked when the
- * specified wall clock time @datetime is reached. This includes
- * events such as the system clock being set past the given time.
- *
- * Compare versus g_timeout_source_new() which is defined to use
- * monotonic time as returned by g_get_monotonic_time().
- *
- * If @cancel_on_set is given, the callback will also be invoked at
- * most a second after the system clock is changed. This includes
- * being set backwards or forwards, and system
- * resume from suspend. Not all operating systems allow detecting all
- * relevant events efficiently - this function may cause the process
- * to wake up once a second in those cases.
- *
- * A wall clock display should use @cancel_on_set; a calendaring
- * program shouldn't need to.
- *
- * Note that the return value from the associated callback will be
- * ignored; this is a one time watch.
- *
- * <note><para>This function currently does not detect time zone
- * changes. On Linux, your program should also monitor the
- * <literal>/etc/timezone</literal> file using
- * #GFileMonitor.</para></note>
-*
- * <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>
- *
- * Return value: A newly-constructed #GSource
- *
- * Since: 2.30
- **/
-GSource *
-g_date_time_source_new (GDateTime *datetime,
- gboolean cancel_on_set)
-{
- GDateTimeSource *datetime_source;
- gint64 unix_seconds;
-
- unix_seconds = g_date_time_to_unix (datetime);
-
- datetime_source = (GDateTimeSource*) g_source_new (&g_datetime_source_funcs, sizeof (GDateTimeSource));
-
- datetime_source->cancel_on_set = cancel_on_set;
-
-#ifdef HAVE_TIMERFD
- if (g_datetime_source_init_timerfd (datetime_source, unix_seconds))
- return (GSource*)datetime_source;
- /* Fall through to non-timerfd code */
-#endif
-
- datetime_source->real_expiration = unix_seconds * 1000000;
- g_datetime_source_reschedule (datetime_source, g_get_monotonic_time ());
-
- return (GSource*)datetime_source;
-}
-
/* Epilogue {{{1 */
/* vim:set foldmethod=marker: */
g_main_loop_run (loop);
}
-static gboolean
-on_test_date_time_watch_timeout (gpointer user_data)
-{
- *((gboolean*)user_data) = TRUE;
-
- g_main_loop_quit (loop);
-
- return TRUE;
-}
-
-/* This test isn't very useful; it's hard to actually test much of the
- * functionality of g_date_time_source_new() without a means to set
- * the system clock (which typically requires system-specific
- * interfaces as well as elevated privileges).
- *
- * But at least we're running the code and ensuring the timer fires.
- */
-static void
-test_date_time_create_watch (gboolean cancel_on_set)
-{
- GSource *source;
- GDateTime *now, *expiry;
- gboolean fired = FALSE;
- gint64 orig_time_monotonic, end_time_monotonic;
- gint64 elapsed_monotonic_seconds;
-
- loop = g_main_loop_new (NULL, FALSE);
-
- orig_time_monotonic = g_get_monotonic_time ();
-
- now = g_date_time_new_now_local ();
- expiry = g_date_time_add_seconds (now, 7);
- g_date_time_unref (now);
-
- source = g_date_time_source_new (expiry, cancel_on_set);
- g_source_set_callback (source, on_test_date_time_watch_timeout, &fired, NULL);
- g_source_attach (source, NULL);
- g_source_unref (source);
-
- g_main_loop_run (loop);
-
- g_assert (fired);
- if (!cancel_on_set)
- {
- end_time_monotonic = g_get_monotonic_time ();
-
- elapsed_monotonic_seconds = 1 + (end_time_monotonic - orig_time_monotonic) / G_TIME_SPAN_SECOND;
-
- g_assert_cmpint (elapsed_monotonic_seconds, >=, 7);
- }
- else
- {
- /* We can't really assert much about the cancel_on_set case */
- }
-}
-
-static void
-test_date_time_create_watch_nocancel_on_set (void)
-{
- test_date_time_create_watch (FALSE);
-}
-
-static void
-test_date_time_create_watch_cancel_on_set (void)
-{
- test_date_time_create_watch (TRUE);
-}
-
int
main (int argc, char *argv[])
{
g_test_add_func ("/timeout/seconds", test_seconds);
g_test_add_func ("/timeout/rounding", test_rounding);
- g_test_add_func ("/timeout/datetime_watch_nocancel_on_set", test_date_time_create_watch_nocancel_on_set);
- g_test_add_func ("/timeout/datetime_watch_cancel_on_set", test_date_time_create_watch_cancel_on_set);
return g_test_run ();
}