1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /*-
3 * Copyright (c) 2011 Jannis Pohlmann <jannis@xfce.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <glib-object.h>
28
29 #include <tumbler/tumbler.h>
30
31 #include <tumblerd/tumbler-lifecycle-manager.h>
32 #include <tumblerd/tumbler-utils.h>
33
34
35
36 #define SHUTDOWN_TIMEOUT_SECONDS 300
37
38
39
40 /* signal identifiers */
41 enum
42 {
43 SIGNAL_SHUTDOWN,
44 LAST_SIGNAL,
45 };
46
47
48
49 static void tumbler_lifecycle_manager_finalize (GObject *object);
50
51
52
53 struct _TumblerLifecycleManagerClass
54 {
55 GObjectClass __parent__;
56 };
57
58 struct _TumblerLifecycleManager
59 {
60 GObject __parent__;
61
62 TUMBLER_MUTEX (lock);
63
64 guint timeout_id;
65 guint component_use_count;
66 guint shutdown_emitted : 1;
67 };
68
69
70
71 static guint lifecycle_manager_signals[LAST_SIGNAL];
72
73
74
G_DEFINE_TYPE(TumblerLifecycleManager,tumbler_lifecycle_manager,G_TYPE_OBJECT)75 G_DEFINE_TYPE (TumblerLifecycleManager, tumbler_lifecycle_manager, G_TYPE_OBJECT)
76
77
78
79 static void
80 tumbler_lifecycle_manager_class_init (TumblerLifecycleManagerClass *klass)
81 {
82 GObjectClass *gobject_class;
83
84 /* Determine the parent type class */
85 tumbler_lifecycle_manager_parent_class = g_type_class_peek_parent (klass);
86
87 gobject_class = G_OBJECT_CLASS (klass);
88 gobject_class->finalize = tumbler_lifecycle_manager_finalize;
89
90 lifecycle_manager_signals[SIGNAL_SHUTDOWN] =
91 g_signal_new ("shutdown",
92 TUMBLER_TYPE_LIFECYCLE_MANAGER,
93 G_SIGNAL_RUN_LAST,
94 0,
95 NULL,
96 NULL,
97 g_cclosure_marshal_VOID__VOID,
98 G_TYPE_NONE,
99 0);
100 }
101
102
103
104 static void
tumbler_lifecycle_manager_init(TumblerLifecycleManager * manager)105 tumbler_lifecycle_manager_init (TumblerLifecycleManager *manager)
106 {
107 tumbler_mutex_create (manager->lock);
108 manager->timeout_id = 0;
109 manager->component_use_count = 0;
110 manager->shutdown_emitted = FALSE;
111 }
112
113
114
115 static void
tumbler_lifecycle_manager_finalize(GObject * object)116 tumbler_lifecycle_manager_finalize (GObject *object)
117 {
118 TumblerLifecycleManager *manager = TUMBLER_LIFECYCLE_MANAGER (object);
119
120 tumbler_mutex_free (manager->lock);
121
122 (*G_OBJECT_CLASS (tumbler_lifecycle_manager_parent_class)->finalize) (object);
123 }
124
125
126
127 static gboolean
tumbler_lifecycle_manager_timeout(TumblerLifecycleManager * manager)128 tumbler_lifecycle_manager_timeout (TumblerLifecycleManager *manager)
129 {
130 g_return_val_if_fail (TUMBLER_IS_LIFECYCLE_MANAGER (manager), FALSE);
131
132 tumbler_mutex_lock (manager->lock);
133
134 /* reschedule the timeout if one of the components is still in use */
135 if (manager->component_use_count > 0)
136 {
137 tumbler_mutex_unlock (manager->lock);
138 return TRUE;
139 }
140
141 /* reset the timeout id */
142 manager->timeout_id = 0;
143
144 /* emit the shutdown signal */
145 g_signal_emit (manager, lifecycle_manager_signals[SIGNAL_SHUTDOWN], 0);
146
147 /* set the shutdown emitted flag to force other threads not to
148 * reschedule the timeout */
149 manager->shutdown_emitted = TRUE;
150
151 tumbler_mutex_unlock (manager->lock);
152
153 return FALSE;
154 }
155
156
157
158 TumblerLifecycleManager *
tumbler_lifecycle_manager_new(void)159 tumbler_lifecycle_manager_new (void)
160 {
161 return g_object_new (TUMBLER_TYPE_LIFECYCLE_MANAGER, NULL);
162 }
163
164
165
166 void
tumbler_lifecycle_manager_start(TumblerLifecycleManager * manager)167 tumbler_lifecycle_manager_start (TumblerLifecycleManager *manager)
168 {
169 g_return_if_fail (TUMBLER_IS_LIFECYCLE_MANAGER (manager));
170
171 tumbler_mutex_lock (manager->lock);
172
173 /* ignore if there already is a timeout scheduled */
174 if (manager->timeout_id > 0)
175 {
176 tumbler_mutex_unlock (manager->lock);
177 return;
178 }
179
180 manager->timeout_id =
181 g_timeout_add_seconds (SHUTDOWN_TIMEOUT_SECONDS,
182 (GSourceFunc) tumbler_lifecycle_manager_timeout,
183 manager);
184
185 tumbler_mutex_unlock (manager->lock);
186 }
187
188
189
190 gboolean
tumbler_lifecycle_manager_keep_alive(TumblerLifecycleManager * manager,GError ** error)191 tumbler_lifecycle_manager_keep_alive (TumblerLifecycleManager *manager,
192 GError **error)
193 {
194 g_return_val_if_fail (TUMBLER_IS_LIFECYCLE_MANAGER (manager), FALSE);
195 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
196
197 tumbler_mutex_lock (manager->lock);
198
199 /* if the shutdown signal has been emitted, there's nothing
200 * we can do to prevent a shutdown anymore */
201 if (manager->shutdown_emitted)
202 {
203 tumbler_mutex_unlock (manager->lock);
204
205 if (error != NULL)
206 {
207 g_set_error (error, TUMBLER_ERROR, TUMBLER_ERROR_SHUTTING_DOWN,
208 "%s", _("The thumbnailer service is shutting down"));
209 }
210 return FALSE;
211 }
212
213 /* if there is an existing timeout, drop it (we are going to
214 * replace it with a new one) */
215 if (manager->timeout_id > 0)
216 g_source_remove (manager->timeout_id);
217
218 /* reschedule the shutdown timeout */
219 manager->timeout_id =
220 g_timeout_add_seconds (SHUTDOWN_TIMEOUT_SECONDS,
221 (GSourceFunc) tumbler_lifecycle_manager_timeout,
222 manager);
223
224 tumbler_mutex_unlock (manager->lock);
225
226 return TRUE;
227 }
228
229
230
231 void
tumbler_lifecycle_manager_increment_use_count(TumblerLifecycleManager * manager)232 tumbler_lifecycle_manager_increment_use_count (TumblerLifecycleManager *manager)
233 {
234 g_return_if_fail (TUMBLER_IS_LIFECYCLE_MANAGER (manager));
235
236 tumbler_mutex_lock (manager->lock);
237
238 manager->component_use_count += 1;
239
240 tumbler_mutex_unlock (manager->lock);
241 }
242
243
244
245 void
tumbler_lifecycle_manager_decrement_use_count(TumblerLifecycleManager * manager)246 tumbler_lifecycle_manager_decrement_use_count (TumblerLifecycleManager *manager)
247 {
248 g_return_if_fail (TUMBLER_IS_LIFECYCLE_MANAGER (manager));
249
250 tumbler_mutex_lock (manager->lock);
251
252 /* decrement the use count, make sure not to drop below zero */
253 if (manager->component_use_count > 0)
254 manager->component_use_count -= 1;
255
256 tumbler_mutex_unlock (manager->lock);
257 }
258