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