1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2019 Joël Krähemann
3  *
4  * This file is part of GSequencer.
5  *
6  * GSequencer is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSequencer is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSequencer.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <ags/thread/ags_destroy_worker.h>
21 
22 #include <ags/object/ags_connectable.h>
23 
24 #include <stdlib.h>
25 
26 void ags_destroy_worker_class_init(AgsDestroyWorkerClass *destroy_worker);
27 void ags_destroy_worker_init(AgsDestroyWorker *destroy_worker);
28 void ags_destroy_worker_finalize(GObject *gobject);
29 
30 void ags_destroy_worker_start(AgsThread *thread);
31 void ags_destroy_worker_stop(AgsThread *thread);
32 
33 void ags_destroy_worker_do_poll(AgsWorkerThread *worker_thread);
34 
35 /**
36  * SECTION:ags_destroy_worker
37  * @short_description: destroy worker
38  * @title: AgsDestroyWorker
39  * @section_id:
40  * @include: ags/worker/ags_destroy_worker.h
41  *
42  * The #AgsDestroyWorker does non-realtime work. You might want
43  * give it the responsibility to destroy your objects.
44  */
45 
46 static gpointer ags_destroy_worker_parent_class = NULL;
47 
48 AgsDestroyWorker *ags_destroy_worker = NULL;
49 
50 GType
ags_destroy_worker_get_type()51 ags_destroy_worker_get_type()
52 {
53   static volatile gsize g_define_type_id__volatile = 0;
54 
55   if(g_once_init_enter (&g_define_type_id__volatile)){
56     GType ags_type_destroy_worker = 0;
57 
58     static const GTypeInfo ags_destroy_worker_info = {
59       sizeof (AgsDestroyWorkerClass),
60       NULL, /* base_init */
61       NULL, /* base_finalize */
62       (GClassInitFunc) ags_destroy_worker_class_init,
63       NULL, /* class_finalize */
64       NULL, /* class_data */
65       sizeof (AgsDestroyWorker),
66       0,    /* n_preallocs */
67       (GInstanceInitFunc) ags_destroy_worker_init,
68     };
69 
70     ags_type_destroy_worker = g_type_register_static(AGS_TYPE_WORKER_THREAD,
71 						     "AgsDestroyWorker",
72 						     &ags_destroy_worker_info,
73 						     0);
74 
75     g_once_init_leave(&g_define_type_id__volatile, ags_type_destroy_worker);
76   }
77 
78   return g_define_type_id__volatile;
79 }
80 
81 void
ags_destroy_worker_class_init(AgsDestroyWorkerClass * destroy_worker)82 ags_destroy_worker_class_init(AgsDestroyWorkerClass *destroy_worker)
83 {
84   GObjectClass *gobject;
85   AgsThreadClass *thread;
86   AgsWorkerThreadClass *worker_thread;
87 
88   ags_destroy_worker_parent_class = g_type_class_peek_parent(destroy_worker);
89 
90   /* GObject */
91   gobject = (GObjectClass *) destroy_worker;
92 
93   gobject->finalize = ags_destroy_worker_finalize;
94 
95   /* AgsThread */
96   thread = (AgsThreadClass *) destroy_worker;
97 
98   thread->start = ags_destroy_worker_start;
99   thread->run = NULL;
100   thread->stop = ags_destroy_worker_stop;
101 
102   /* AgsDestroyWorker */
103   worker_thread = (AgsWorkerThreadClass *) destroy_worker;
104 
105   worker_thread->do_poll = ags_destroy_worker_do_poll;
106 }
107 
108 void
ags_destroy_worker_init(AgsDestroyWorker * destroy_worker)109 ags_destroy_worker_init(AgsDestroyWorker *destroy_worker)
110 {
111   destroy_worker->destroy_interval = (struct timespec *) malloc(sizeof(struct timespec));
112 
113   destroy_worker->destroy_interval->tv_sec = 1;
114   destroy_worker->destroy_interval->tv_nsec = 0;
115 
116   /* lock destroy list */
117   g_rec_mutex_init(&(destroy_worker->destroy_mutex));
118 
119   /* destroy list */
120   destroy_worker->destroy_list = NULL;
121 }
122 
123 void
ags_destroy_worker_finalize(GObject * gobject)124 ags_destroy_worker_finalize(GObject *gobject)
125 {
126   AgsDestroyWorker *destroy_worker;
127 
128   destroy_worker = AGS_DESTROY_WORKER(gobject);
129 
130   if(destroy_worker->destroy_interval != NULL){
131     free(destroy_worker->destroy_interval);
132   }
133 
134   /* call parent */
135   G_OBJECT_CLASS(ags_destroy_worker_parent_class)->finalize(gobject);
136 }
137 
138 void
ags_destroy_worker_start(AgsThread * thread)139 ags_destroy_worker_start(AgsThread *thread)
140 {
141   AgsWorkerThread *worker_thread;
142 
143   worker_thread = AGS_WORKER_THREAD(thread);
144 
145   ags_worker_thread_set_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUNNING);
146 
147   worker_thread->worker_thread = g_thread_new("Advanced Gtk+ Sequencer - destroy worker",
148 					      ags_woker_thread_do_poll_loop,
149 					      worker_thread);
150 }
151 
152 void
ags_destroy_worker_stop(AgsThread * thread)153 ags_destroy_worker_stop(AgsThread *thread)
154 {
155   AgsWorkerThread *worker_thread;
156 
157   worker_thread = AGS_WORKER_THREAD(thread);
158 
159   ags_worker_thread_set_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUNNING);
160 }
161 
162 void
ags_destroy_worker_do_poll(AgsWorkerThread * worker_thread)163 ags_destroy_worker_do_poll(AgsWorkerThread *worker_thread)
164 {
165   AgsDestroyWorker *destroy_worker;
166 
167   GList *list, *list_start;
168 
169   destroy_worker = AGS_DESTROY_WORKER(worker_thread);
170 
171   g_rec_mutex_lock(&(destroy_worker->destroy_mutex));
172 
173   list_start =
174     list = destroy_worker->destroy_list;
175   destroy_worker->destroy_list = NULL;
176 
177   g_rec_mutex_unlock(&(destroy_worker->destroy_mutex));
178 
179   while(list != NULL){
180     AGS_DESTROY_ENTRY(list->data)->destroy_func(AGS_DESTROY_ENTRY(list->data)->ptr);
181 
182     list = list->next;
183   }
184 
185   g_list_free_full(list_start,
186 		   g_free);
187 
188   nanosleep(destroy_worker->destroy_interval,
189 	    NULL);
190 }
191 
192 /**
193  * ags_destroy_entry_alloc:
194  * @ptr: a pointer
195  * @destroy_func: (scope call): the @ptr's destroy function
196  *
197  * Allocated a destroy entry.
198  *
199  * Returns: (type gpointer) (transfer none): the allocated #AgsDestroyEntry
200  *
201  * Since: 3.0.0
202  */
203 AgsDestroyEntry*
ags_destroy_entry_alloc(gpointer ptr,AgsDestroyFunc destroy_func)204 ags_destroy_entry_alloc(gpointer ptr, AgsDestroyFunc destroy_func)
205 {
206   AgsDestroyEntry *destroy_entry;
207 
208   destroy_entry = (AgsDestroyEntry *) malloc(sizeof(AgsDestroyEntry));
209 
210   destroy_entry->ptr = ptr;
211   destroy_entry->destroy_func = destroy_func;
212 
213   return(destroy_entry);
214 }
215 
216 /**
217  * ags_destroy_worker_add:
218  * @destroy_worker: (type gpointer): the #AgsDestroyWorker
219  * @ptr: the gpointer to destroy
220  * @destroy_func: (scope call): the AgsDestroyFunc
221  *
222  * Add @ptr for destruction using @destroy_func.
223  *
224  * Since: 3.0.0
225  */
226 void
ags_destroy_worker_add(AgsDestroyWorker * destroy_worker,gpointer ptr,AgsDestroyFunc destroy_func)227 ags_destroy_worker_add(AgsDestroyWorker *destroy_worker,
228 		       gpointer ptr, AgsDestroyFunc destroy_func)
229 {
230   AgsDestroyEntry *destroy_entry;
231 
232   if(!AGS_IS_DESTROY_WORKER(destroy_worker) ||
233      ptr == NULL ||
234      destroy_func == NULL){
235     return;
236   }
237 
238   destroy_entry = ags_destroy_entry_alloc(ptr, destroy_func);
239 
240   g_rec_mutex_lock(&(destroy_worker->destroy_mutex));
241 
242   destroy_worker->destroy_list = g_list_prepend(destroy_worker->destroy_list,
243 						destroy_entry);
244 
245   g_rec_mutex_unlock(&(destroy_worker->destroy_mutex));
246 }
247 
248 /**
249  * ags_destroy_worker_get_instance:
250  *
251  * Get your destroy worker instance.
252  *
253  * Returns: (transfer none): the #AgsDestroyWorker instance
254  *
255  * Since: 3.0.0
256  */
257 AgsDestroyWorker*
ags_destroy_worker_get_instance()258 ags_destroy_worker_get_instance()
259 {
260   static GMutex mutex;
261 
262   g_mutex_lock(&mutex);
263 
264   if(ags_destroy_worker == NULL){
265     ags_destroy_worker = ags_destroy_worker_new();
266   }
267 
268   g_mutex_unlock(&mutex);
269 
270   return(ags_destroy_worker);
271 }
272 
273 /**
274  * ags_destroy_worker_new:
275  *
276  * Create a new #AgsDestroyWorker.
277  *
278  * Returns: the new #AgsDestroyWorker
279  *
280  * Since: 3.0.0
281  */
282 AgsDestroyWorker*
ags_destroy_worker_new()283 ags_destroy_worker_new()
284 {
285   AgsDestroyWorker *destroy_worker;
286 
287   destroy_worker = (AgsDestroyWorker *) g_object_new(AGS_TYPE_DESTROY_WORKER,
288 						     NULL);
289 
290   return(destroy_worker);
291 }
292