1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2020 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_returnable_thread.h>
21 
22 #include <ags/object/ags_connectable.h>
23 
24 #include <ags/thread/ags_thread_pool.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 
29 #include <math.h>
30 
31 #include <unistd.h>
32 #include <errno.h>
33 
34 #include <ags/i18n.h>
35 
36 void ags_returnable_thread_class_init(AgsReturnableThreadClass *returnable_thread);
37 void ags_returnable_thread_init(AgsReturnableThread *returnable_thread);
38 void ags_returnable_thread_set_property(GObject *gobject,
39 					guint prop_id,
40 					const GValue *value,
41 					GParamSpec *param_spec);
42 void ags_returnable_thread_get_property(GObject *gobject,
43 					guint prop_id,
44 					GValue *value,
45 					GParamSpec *param_spec);
46 void ags_returnable_thread_dispose(GObject *gobject);
47 void ags_returnable_thread_finalize(GObject *gobject);
48 
49 void ags_returnable_thread_start(AgsThread *thread);
50 void ags_returnable_thread_run(AgsThread *thread);
51 void ags_returnable_thread_stop(AgsThread *thread);
52 void ags_returnable_thread_resume(AgsThread *thread);
53 
54 /**
55  * SECTION:ags_returnable_thread
56  * @short_description: returnable thread
57  * @title: AgsReturnableThread
58  * @section_id:
59  * @include: ags/thread/ags_returnable_thread.h
60  *
61  * The #AgsReturnableThread acts as thread. It should return after a short
62  * while because of limited thread pool.
63  */
64 
65 enum{
66   PROP_0,
67   PROP_THREAD_POOL,
68   PROP_SAFE_DATA,
69 };
70 
71 enum{
72   SAFE_RUN,
73   LAST_SIGNAL,
74 };
75 
76 static gpointer ags_returnable_thread_parent_class = NULL;
77 static guint returnable_thread_signals[LAST_SIGNAL];
78 
79 GType
ags_returnable_thread_get_type()80 ags_returnable_thread_get_type()
81 {
82   static volatile gsize g_define_type_id__volatile = 0;
83 
84   if(g_once_init_enter (&g_define_type_id__volatile)){
85     GType ags_type_returnable_thread = 0;
86 
87     static const GTypeInfo ags_returnable_thread_info = {
88       sizeof (AgsReturnableThreadClass),
89       NULL, /* base_init */
90       NULL, /* base_finalize */
91       (GClassInitFunc) ags_returnable_thread_class_init,
92       NULL, /* class_finalize */
93       NULL, /* class_data */
94       sizeof (AgsReturnableThread),
95       0,    /* n_preallocs */
96       (GInstanceInitFunc) ags_returnable_thread_init,
97     };
98 
99     ags_type_returnable_thread = g_type_register_static(AGS_TYPE_THREAD,
100 							"AgsReturnableThread",
101 							&ags_returnable_thread_info,
102 							0);
103 
104     g_once_init_leave(&g_define_type_id__volatile, ags_type_returnable_thread);
105   }
106 
107   return g_define_type_id__volatile;
108 }
109 
110 GType
ags_returnable_thread_flags_get_type()111 ags_returnable_thread_flags_get_type()
112 {
113   static volatile gsize g_flags_type_id__volatile;
114 
115   if(g_once_init_enter (&g_flags_type_id__volatile)){
116     static const GFlagsValue values[] = {
117       { AGS_RETURNABLE_THREAD_IN_USE, "AGS_RETURNABLE_THREAD_IN_USE", "returnable-thread-in-use" },
118       { AGS_RETURNABLE_THREAD_RESET, "AGS_RETURNABLE_THREAD_RESET", "returnable-thread-reset" },
119       { AGS_RETURNABLE_THREAD_RUN_ONCE, "AGS_RETURNABLE_THREAD_RUN_ONCE", "returnable-thread-run-once" },
120       { 0, NULL, NULL }
121     };
122 
123     GType g_flags_type_id = g_flags_register_static(g_intern_static_string("AgsReturnableThreadFlags"), values);
124 
125     g_once_init_leave (&g_flags_type_id__volatile, g_flags_type_id);
126   }
127 
128   return g_flags_type_id__volatile;
129 }
130 
131 void
ags_returnable_thread_class_init(AgsReturnableThreadClass * returnable_thread)132 ags_returnable_thread_class_init(AgsReturnableThreadClass *returnable_thread)
133 {
134   AgsThreadClass *thread;
135 
136   GObjectClass *gobject;
137 
138   GParamSpec *param_spec;
139 
140   ags_returnable_thread_parent_class = g_type_class_peek_parent(returnable_thread);
141 
142   /* GObjectClass */
143   gobject = (GObjectClass *) returnable_thread;
144 
145   gobject->set_property = ags_returnable_thread_set_property;
146   gobject->get_property = ags_returnable_thread_get_property;
147 
148   gobject->dispose = ags_returnable_thread_dispose;
149   gobject->finalize = ags_returnable_thread_finalize;
150 
151   /* properties */
152   /**
153    * AgsReturnableThread:thread-pool:
154    *
155    * The assigned #AgsThreadPool providing default settings.
156    *
157    * Since: 3.0.0
158    */
159   param_spec = g_param_spec_object("thread-pool",
160 				   i18n_pspec("assigned thread pool"),
161 				   i18n_pspec("The thread pool it is assigned with"),
162 				   G_TYPE_OBJECT,
163 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
164   g_object_class_install_property(gobject,
165 				  PROP_THREAD_POOL,
166 				  param_spec);
167 
168   /* AgsThreadClass */
169   thread = (AgsThreadClass *) returnable_thread;
170 
171   thread->start = ags_returnable_thread_start;
172   thread->run = ags_returnable_thread_run;
173   thread->stop = ags_returnable_thread_stop;
174 
175   /* AgsReturnableThreadClass */
176   returnable_thread->safe_run = NULL;
177 
178   /* signals */
179   /**
180    * AgsReturnableThread::safe-run:
181    * @returnable_thread: the #AgsReturnableThread
182    *
183    * The ::safe-run is invoked durin AgsThread::run as
184    * a context safe wrapper.
185    *
186    * Since: 3.0.0
187    */
188   returnable_thread_signals[SAFE_RUN] =
189     g_signal_new("safe-run",
190 		 G_TYPE_FROM_CLASS (returnable_thread),
191 		 G_SIGNAL_RUN_LAST,
192 		 G_STRUCT_OFFSET (AgsReturnableThreadClass, safe_run),
193 		 NULL, NULL,
194 		 g_cclosure_marshal_VOID__VOID,
195 		 G_TYPE_NONE, 0);
196 
197 }
198 
199 void
ags_returnable_thread_init(AgsReturnableThread * returnable_thread)200 ags_returnable_thread_init(AgsReturnableThread *returnable_thread)
201 {
202   AgsThread *thread;
203 
204   thread = AGS_THREAD(returnable_thread);
205 
206   ags_thread_set_flags(thread, AGS_THREAD_UNREF_ON_EXIT);
207 
208   g_object_set(thread,
209 	       "frequency", AGS_RETURNABLE_THREAD_DEFAULT_JIFFIE,
210 	       NULL);
211 
212   g_atomic_int_set(&(returnable_thread->flags),
213 		   AGS_RETURNABLE_THREAD_RUN_ONCE);
214 
215   returnable_thread->thread_pool = NULL;
216 
217   g_rec_mutex_init(&(returnable_thread->reset_mutex));
218 
219   g_atomic_pointer_set(&(returnable_thread->safe_data),
220 		       NULL);
221 
222   returnable_thread->handler = 0;
223 }
224 
225 void
ags_returnable_thread_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)226 ags_returnable_thread_set_property(GObject *gobject,
227 				   guint prop_id,
228 				   const GValue *value,
229 				   GParamSpec *param_spec)
230 {
231   AgsReturnableThread *returnable_thread;
232 
233   returnable_thread = AGS_RETURNABLE_THREAD(gobject);
234 
235   switch(prop_id){
236   case PROP_THREAD_POOL:
237     {
238       GObject *thread_pool;
239 
240       thread_pool = g_value_get_object(value);
241 
242       if(returnable_thread->thread_pool == thread_pool)
243 	return;
244 
245       if(returnable_thread->thread_pool != NULL){
246 	g_object_unref(returnable_thread->thread_pool);
247       }
248 
249       if(thread_pool != NULL){
250 	g_object_ref(thread_pool);
251       }
252 
253       returnable_thread->thread_pool = thread_pool;
254     }
255     break;
256   default:
257     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
258     break;
259   }
260 }
261 
262 void
ags_returnable_thread_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)263 ags_returnable_thread_get_property(GObject *gobject,
264 				   guint prop_id,
265 				   GValue *value,
266 				   GParamSpec *param_spec)
267 {
268   AgsReturnableThread *returnable_thread;
269 
270   returnable_thread = AGS_RETURNABLE_THREAD(gobject);
271 
272   switch(prop_id){
273   case PROP_THREAD_POOL:
274     {
275       g_value_set_object(value, returnable_thread->thread_pool);
276     }
277     break;
278   default:
279     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
280     break;
281   }
282 }
283 
284 void
ags_returnable_thread_dispose(GObject * gobject)285 ags_returnable_thread_dispose(GObject *gobject)
286 {
287   AgsReturnableThread *returnable_thread;
288 
289   returnable_thread = (AgsReturnableThread *) gobject;
290 
291   /* thread pool */
292   if(returnable_thread->thread_pool != NULL){
293     g_object_unref(returnable_thread->thread_pool);
294 
295     returnable_thread->thread_pool = NULL;
296   }
297 
298   /* call parent */
299   G_OBJECT_CLASS(ags_returnable_thread_parent_class)->dispose(gobject);
300 }
301 
302 void
ags_returnable_thread_finalize(GObject * gobject)303 ags_returnable_thread_finalize(GObject *gobject)
304 {
305   AgsReturnableThread *returnable_thread;
306 
307   returnable_thread = (AgsReturnableThread *) gobject;
308 
309   /* thread pool */
310   if(returnable_thread->thread_pool != NULL){
311     g_object_unref(returnable_thread->thread_pool);
312   }
313 
314   /* call parent */
315   G_OBJECT_CLASS(ags_returnable_thread_parent_class)->finalize(gobject);
316 }
317 
318 void
ags_returnable_thread_start(AgsThread * thread)319 ags_returnable_thread_start(AgsThread *thread)
320 {
321   AGS_THREAD_CLASS(ags_returnable_thread_parent_class)->start(thread);
322 }
323 
324 void
ags_returnable_thread_run(AgsThread * thread)325 ags_returnable_thread_run(AgsThread *thread)
326 {
327   AgsThreadPool *thread_pool;
328   AgsReturnableThread *returnable_thread;
329 
330   GRecMutex *thread_mutex;
331 
332   //  g_message("reset:0");
333 
334   /* retrieve some variables */
335   returnable_thread = AGS_RETURNABLE_THREAD(thread);
336 
337   /* safe run */
338   if((AGS_RETURNABLE_THREAD_IN_USE & (g_atomic_int_get(&(returnable_thread->flags)))) != 0){
339     ags_returnable_thread_safe_run(returnable_thread);
340 
341     if((AGS_RETURNABLE_THREAD_RUN_ONCE & (g_atomic_int_get(&(returnable_thread->flags)))) != 0){
342       g_atomic_int_and(&(returnable_thread->flags),
343 		       (~AGS_RETURNABLE_THREAD_IN_USE));
344 
345       /* return to thread pool */
346       thread_mutex = AGS_THREAD_GET_OBJ_MUTEX(thread);
347 
348       g_rec_mutex_lock(thread_mutex);
349 
350       thread_pool = returnable_thread->thread_pool;
351 
352       g_rec_mutex_unlock(thread_mutex);
353 
354       /* give returnable thread back to thread pool */
355       g_atomic_pointer_set(&(returnable_thread->safe_data),
356 			   NULL);
357       ags_returnable_thread_disconnect_safe_run(returnable_thread);
358 
359       g_atomic_pointer_set(&(thread_pool->returnable_thread),
360 			   g_list_prepend(g_atomic_pointer_get(&(thread_pool->returnable_thread)),
361 					  returnable_thread));
362     }
363   }
364 }
365 
366 void
ags_returnable_thread_safe_run(AgsReturnableThread * returnable_thread)367 ags_returnable_thread_safe_run(AgsReturnableThread *returnable_thread)
368 {
369   guint returnable_thread_signal;
370 
371   returnable_thread_signal = returnable_thread_signals[SAFE_RUN];
372 
373   g_return_if_fail(AGS_IS_RETURNABLE_THREAD(returnable_thread));
374 
375   g_object_ref(returnable_thread);
376   g_signal_emit(returnable_thread,
377 		returnable_thread_signal, 0);
378   g_object_unref(returnable_thread);
379 }
380 
381 void
ags_returnable_thread_stop(AgsThread * thread)382 ags_returnable_thread_stop(AgsThread *thread)
383 {
384   AGS_THREAD_CLASS(ags_returnable_thread_parent_class)->stop(thread);
385 }
386 
387 void
ags_returnable_thread_resume(AgsThread * thread)388 ags_returnable_thread_resume(AgsThread *thread)
389 {
390   /* empty */
391 }
392 
393 /**
394  * ags_returnable_thread_test_flags:
395  * @returnable_thread: the #AgsReturnableThread
396  * @flags: the flags
397  *
398  * Test @flags to be set on @returnable_thread.
399  *
400  * Returns: %TRUE if flags are set, else %FALSE
401  *
402  * Since: 3.0.0
403  */
404 gboolean
ags_returnable_thread_test_flags(AgsReturnableThread * returnable_thread,guint flags)405 ags_returnable_thread_test_flags(AgsReturnableThread *returnable_thread, guint flags)
406 {
407   gboolean retval;
408 
409   if(!AGS_IS_RETURNABLE_THREAD(returnable_thread)){
410     return(FALSE);
411   }
412 
413   retval = ((flags & (g_atomic_int_get(&(returnable_thread->flags)))) != 0) ? TRUE: FALSE;
414 
415   return(retval);
416 }
417 
418 /**
419  * ags_returnable_thread_set_flags:
420  * @returnable_thread: the #AgsReturnableThread
421  * @flags: the flags
422  *
423  * Set flags.
424  *
425  * Since: 3.0.0
426  */
427 void
ags_returnable_thread_set_flags(AgsReturnableThread * returnable_thread,guint flags)428 ags_returnable_thread_set_flags(AgsReturnableThread *returnable_thread, guint flags)
429 {
430   if(!AGS_IS_RETURNABLE_THREAD(returnable_thread)){
431     return;
432   }
433 
434   g_atomic_int_or(&(returnable_thread->flags), flags);
435 }
436 
437 /**
438  * ags_returnable_thread_unset_flags:
439  * @returnable_thread: the #AgsReturnableThread
440  * @flags: the flags
441  *
442  * Unset flags.
443  *
444  * Since: 3.0.0
445  */
446 void
ags_returnable_thread_unset_flags(AgsReturnableThread * returnable_thread,guint flags)447 ags_returnable_thread_unset_flags(AgsReturnableThread *returnable_thread, guint flags)
448 {
449   if(!AGS_IS_RETURNABLE_THREAD(returnable_thread)){
450     return;
451   }
452 
453   g_atomic_int_and(&(returnable_thread->flags), (~flags));
454 }
455 
456 /**
457  * ags_returnable_thread_connect_safe_run:
458  * @returnable_thread: the thread to connect
459  * @callback: (scope call): the callback
460  *
461  * Connects @callback to @thread.
462  *
463  * Since: 3.0.0
464  */
465 void
ags_returnable_thread_connect_safe_run(AgsReturnableThread * returnable_thread,AgsReturnableThreadCallback callback)466 ags_returnable_thread_connect_safe_run(AgsReturnableThread *returnable_thread, AgsReturnableThreadCallback callback)
467 {
468   if(returnable_thread->handler > 0){
469     return;
470   }
471 
472   returnable_thread->handler = g_signal_connect(G_OBJECT(returnable_thread), "safe-run",
473 						G_CALLBACK(callback), returnable_thread);
474 }
475 
476 /**
477  * ags_returnable_thread_disconnect_safe_run:
478  * @returnable_thread: the thread to disconnect
479  *
480  * Disconnects callback of @thread.
481  *
482  * Since: 3.0.0
483  */
484 void
ags_returnable_thread_disconnect_safe_run(AgsReturnableThread * returnable_thread)485 ags_returnable_thread_disconnect_safe_run(AgsReturnableThread *returnable_thread)
486 {
487   if(returnable_thread->handler == 0){
488     return;
489   }
490 
491   g_signal_handler_disconnect(G_OBJECT(returnable_thread),
492 			      returnable_thread->handler);
493 
494   returnable_thread->handler = 0;
495 }
496 
497 /**
498  * ags_returnable_thread_new:
499  * @thread_pool: the #AgsThreadPool
500  *
501  * Create a new instance of #AgsReturnableThread.
502  *
503  * Returns: the new #AgsReturnableThread
504  *
505  * Since: 3.0.0
506  */
507 AgsReturnableThread*
ags_returnable_thread_new(GObject * thread_pool)508 ags_returnable_thread_new(GObject *thread_pool)
509 {
510   AgsReturnableThread *returnable_thread;
511 
512   returnable_thread = (AgsReturnableThread *) g_object_new(AGS_TYPE_RETURNABLE_THREAD,
513 							   "thread-pool", thread_pool,
514 							   NULL);
515 
516   return(returnable_thread);
517 }
518