Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2018 Endless Mobile, Inc.
4 : : *
5 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2.1 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : *
19 : : * Authors:
20 : : * - Philip Withnall <withnall@endlessm.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <glib.h>
26 : : #include <glib-object.h>
27 : : #include <glib/gi18n-lib.h>
28 : : #include <gio/gio.h>
29 : : #include <libmogwai-schedule/clock.h>
30 : : #include <libmogwai-schedule/clock-system.h>
31 : :
32 : :
33 : : /**
34 : : * SECTION:clock-system
35 : : * @short_description: Implementation of #MwsClock using the system wall clock
36 : : * @stability: Unstable
37 : : * @include: libmogwai-schedule/clock-system.h
38 : : *
39 : : * #MwsClockSystem is the standard implementation of #MwsClock, which uses the
40 : : * system wall clock to provide time and alarms. Internally, it uses
41 : : * g_date_time_new_now_local() to provide time, and
42 : : * g_timeout_source_new_seconds() to provide alarms. It adds #GSources to the
43 : : * thread-default main context from when the #MwsClockSystem was constructed.
44 : : * That main context must be running in order for alarm callbacks to be invoked.
45 : : *
46 : : * FIXME: Currently, this does not support detecting when the system timezone
47 : : * or underlying RTC clock changes, and hence it never emits the
48 : : * #MwsClock::offset-changed signal. See
49 : : * https://phabricator.endlessm.com/T21845.
50 : : *
51 : : * Since: 0.1.0
52 : : */
53 : :
54 : : static void mws_clock_system_clock_init (MwsClockInterface *iface);
55 : :
56 : : static void mws_clock_system_finalize (GObject *obj);
57 : :
58 : : static GDateTime *mws_clock_system_get_now_local (MwsClock *clock);
59 : : static guint mws_clock_system_add_alarm (MwsClock *clock,
60 : : GDateTime *alarm_time,
61 : : GSourceFunc alarm_func,
62 : : gpointer user_data,
63 : : GDestroyNotify destroy_func);
64 : : static void mws_clock_system_remove_alarm (MwsClock *clock,
65 : : guint id);
66 : :
67 : : /**
68 : : * MwsClockSystem:
69 : : *
70 : : * Implementation of #MwsClock which uses the system wall clock.
71 : : *
72 : : * Since: 0.1.0
73 : : */
74 : : struct _MwsClockSystem
75 : : {
76 : : GObject parent;
77 : :
78 : : GMainContext *context; /* (owned) */
79 : : GPtrArray *alarms; /* (owned) (element-type GSource) */
80 : : };
81 : :
82 [ # # # # : 0 : G_DEFINE_TYPE_WITH_CODE (MwsClockSystem, mws_clock_system, G_TYPE_OBJECT,
# # ]
83 : : G_IMPLEMENT_INTERFACE (MWS_TYPE_CLOCK,
84 : : mws_clock_system_clock_init))
85 : : static void
86 : 0 : mws_clock_system_class_init (MwsClockSystemClass *klass)
87 : : {
88 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
89 : :
90 : 0 : object_class->finalize = mws_clock_system_finalize;
91 : 0 : }
92 : :
93 : : static void
94 : 0 : mws_clock_system_clock_init (MwsClockInterface *iface)
95 : : {
96 : 0 : iface->get_now_local = mws_clock_system_get_now_local;
97 : 0 : iface->add_alarm = mws_clock_system_add_alarm;
98 : 0 : iface->remove_alarm = mws_clock_system_remove_alarm;
99 : 0 : }
100 : :
101 : : static void
102 : 0 : destroy_and_unref_source (GSource *source)
103 : : {
104 : 0 : g_source_destroy (source);
105 : 0 : g_source_unref (source);
106 : 0 : }
107 : :
108 : : static void
109 : 0 : mws_clock_system_init (MwsClockSystem *self)
110 : : {
111 : 0 : self->context = g_main_context_ref_thread_default ();
112 : 0 : self->alarms = g_ptr_array_new_with_free_func ((GDestroyNotify) destroy_and_unref_source);
113 : 0 : }
114 : :
115 : : static void
116 : 0 : mws_clock_system_finalize (GObject *obj)
117 : : {
118 : 0 : MwsClockSystem *self = MWS_CLOCK_SYSTEM (obj);
119 : :
120 [ # # ]: 0 : g_clear_pointer (&self->alarms, g_ptr_array_unref);
121 [ # # ]: 0 : g_clear_pointer (&self->context, g_main_context_unref);
122 : :
123 : 0 : G_OBJECT_CLASS (mws_clock_system_parent_class)->finalize (obj);
124 : 0 : }
125 : :
126 : : static GDateTime *
127 : 0 : mws_clock_system_get_now_local (MwsClock *clock)
128 : : {
129 : 0 : return g_date_time_new_now_local ();
130 : : }
131 : :
132 : : static guint
133 : 0 : mws_clock_system_add_alarm (MwsClock *clock,
134 : : GDateTime *alarm_time,
135 : : GSourceFunc alarm_func,
136 : : gpointer user_data,
137 : : GDestroyNotify destroy_func)
138 : : {
139 : 0 : MwsClockSystem *self = MWS_CLOCK_SYSTEM (clock);
140 : :
141 : 0 : g_autoptr(GDateTime) now = mws_clock_system_get_now_local (clock);
142 : :
143 : : /* If the @alarm_time is in the past, invoke the callback on the next main
144 : : * context iteration. */
145 : 0 : GTimeSpan interval = g_date_time_difference (alarm_time, now);
146 [ # # ]: 0 : if (interval < 0)
147 : 0 : interval = 0;
148 : :
149 : 0 : g_autoptr(GSource) source = g_timeout_source_new_seconds (interval / G_USEC_PER_SEC);
150 : 0 : g_source_set_callback (source, alarm_func, user_data, destroy_func);
151 : 0 : g_source_attach (source, self->context);
152 : :
153 : 0 : guint id = g_source_get_id (source);
154 : 0 : g_ptr_array_add (self->alarms, g_steal_pointer (&source));
155 : :
156 : 0 : g_autofree gchar *alarm_time_str = NULL;
157 : 0 : alarm_time_str = g_date_time_format (alarm_time, "%FT%T%:::z");
158 : 0 : g_debug ("%s: Setting alarm %u for %s (in %" G_GUINT64_FORMAT " seconds)",
159 : : G_STRFUNC, id, alarm_time_str, (guint64) interval / G_USEC_PER_SEC);
160 : :
161 : 0 : return id;
162 : : }
163 : :
164 : : static void
165 : 0 : mws_clock_system_remove_alarm (MwsClock *clock,
166 : : guint id)
167 : : {
168 : 0 : MwsClockSystem *self = MWS_CLOCK_SYSTEM (clock);
169 : 0 : GSource *source = g_main_context_find_source_by_id (self->context, id);
170 : :
171 : 0 : g_debug ("%s: Removing alarm %u", G_STRFUNC, id);
172 : :
173 : : /* The source will be destroyed by the free function set up on the array. */
174 [ # # ]: 0 : g_return_if_fail (g_ptr_array_remove_fast (self->alarms, source));
175 : : }
176 : :
177 : : /**
178 : : * mws_clock_system_new:
179 : : *
180 : : * Create a #MwsClockSystem object which gets wall clock time from the system
181 : : * clock.
182 : : *
183 : : * Returns: (transfer full): a new #MwsClockSystem
184 : : * Since: 0.1.0
185 : : */
186 : : MwsClockSystem *
187 : 0 : mws_clock_system_new (void)
188 : : {
189 : 0 : return g_object_new (MWS_TYPE_CLOCK_SYSTEM, NULL);
190 : : }
|