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_worker_thread.h>
21
22 #include <stdlib.h>
23
24 void ags_worker_thread_class_init(AgsWorkerThreadClass *worker_thread);
25 void ags_worker_thread_init(AgsWorkerThread *worker_thread);
26 void ags_worker_thread_finalize(GObject *gobject);
27
28 void ags_worker_thread_start(AgsThread *thread);
29 void ags_worker_thread_run(AgsThread *thread);
30 void ags_worker_thread_stop(AgsThread *thread);
31
32 void* ags_woker_thread_do_poll_loop(void *ptr);
33
34 /**
35 * SECTION:ags_worker_thread
36 * @short_description: worker thread
37 * @title: AgsWorkerThread
38 * @section_id:
39 * @include: ags/thread/ags_worker_thread.h
40 *
41 * The #AgsWorkerThread does non-realtime work. You might want
42 * to synchronize to the run signal within your ::do_poll() method.
43 */
44
45 enum{
46 DO_POLL,
47 LAST_SIGNAL,
48 };
49
50 static gpointer ags_worker_thread_parent_class = NULL;
51 static guint worker_thread_signals[LAST_SIGNAL];
52
53 GType
ags_worker_thread_get_type()54 ags_worker_thread_get_type()
55 {
56 static volatile gsize g_define_type_id__volatile = 0;
57
58 if(g_once_init_enter (&g_define_type_id__volatile)){
59 GType ags_type_worker_thread = 0;
60
61 static const GTypeInfo ags_worker_thread_info = {
62 sizeof (AgsWorkerThreadClass),
63 NULL, /* base_init */
64 NULL, /* base_finalize */
65 (GClassInitFunc) ags_worker_thread_class_init,
66 NULL, /* class_finalize */
67 NULL, /* class_data */
68 sizeof (AgsWorkerThread),
69 0, /* n_preallocs */
70 (GInstanceInitFunc) ags_worker_thread_init,
71 };
72
73 ags_type_worker_thread = g_type_register_static(AGS_TYPE_THREAD,
74 "AgsWorkerThread",
75 &ags_worker_thread_info,
76 0);
77
78 g_once_init_leave(&g_define_type_id__volatile, ags_type_worker_thread);
79 }
80
81 return g_define_type_id__volatile;
82 }
83
84 GType
ags_worker_thread_status_flags_get_type()85 ags_worker_thread_status_flags_get_type()
86 {
87 static volatile gsize g_flags_type_id__volatile;
88
89 if(g_once_init_enter (&g_flags_type_id__volatile)){
90 static const GFlagsValue values[] = {
91 { AGS_WORKER_THREAD_STATUS_RUNNING, "AGS_WORKER_THREAD_STATUS_RUNNING", "worker-thread-status-running" },
92 { AGS_WORKER_THREAD_STATUS_RUN_WAIT, "AGS_WORKER_THREAD_STATUS_RUN_WAIT", "worker-thread-status-run-wait" },
93 { AGS_WORKER_THREAD_STATUS_RUN_DONE, "AGS_WORKER_THREAD_STATUS_RUN_DONE", "worker-thread-status-run-done" },
94 { AGS_WORKER_THREAD_STATUS_RUN_SYNC, "AGS_WORKER_THREAD_STATUS_RUN_SYNC", "worker-thread-status-run-sync" },
95 { 0, NULL, NULL }
96 };
97
98 GType g_flags_type_id = g_flags_register_static(g_intern_static_string("AgsWorkerThreadStatusFlags"), values);
99
100 g_once_init_leave (&g_flags_type_id__volatile, g_flags_type_id);
101 }
102
103 return g_flags_type_id__volatile;
104 }
105
106 void
ags_worker_thread_class_init(AgsWorkerThreadClass * worker_thread)107 ags_worker_thread_class_init(AgsWorkerThreadClass *worker_thread)
108 {
109 GObjectClass *gobject;
110 AgsThreadClass *thread;
111
112 ags_worker_thread_parent_class = g_type_class_peek_parent(worker_thread);
113
114 /* GObject */
115 gobject = (GObjectClass *) worker_thread;
116
117 gobject->finalize = ags_worker_thread_finalize;
118
119 /* AgsThread */
120 thread = (AgsThreadClass *) worker_thread;
121
122 thread->start = ags_worker_thread_start;
123 thread->run = ags_worker_thread_run;
124 thread->stop = ags_worker_thread_stop;
125
126 /* AgsWorkerThread */
127 worker_thread->do_poll = NULL;
128
129 /* signals */
130 /**
131 * AgsWorkerThread::do-poll:
132 * @thread: the #AgsWorkerThread
133 *
134 * The ::do_poll() signal runs independently of ::run() but
135 * might be synchronized using a conditional lock.
136 *
137 * Since: 3.0.0
138 */
139 worker_thread_signals[DO_POLL] =
140 g_signal_new("do-poll",
141 G_TYPE_FROM_CLASS (thread),
142 G_SIGNAL_RUN_LAST,
143 G_STRUCT_OFFSET (AgsWorkerThreadClass, do_poll),
144 NULL, NULL,
145 g_cclosure_marshal_VOID__VOID,
146 G_TYPE_NONE, 0);
147 }
148
149 void
ags_worker_thread_init(AgsWorkerThread * worker_thread)150 ags_worker_thread_init(AgsWorkerThread *worker_thread)
151 {
152 AgsThread *thread;
153
154 thread = (AgsThread *) worker_thread;
155
156 g_object_set(thread,
157 "frequency", AGS_WORKER_THREAD_DEFAULT_JIFFIE,
158 NULL);
159
160 g_atomic_int_set(&(worker_thread->status_flags),
161 0);
162
163 /* synchronization */
164 g_mutex_init(&(worker_thread->run_mutex));
165
166 g_cond_init(&(worker_thread->run_cond));
167
168 /* worker thread */
169 worker_thread->worker_thread = NULL;
170 }
171
172 void
ags_worker_thread_finalize(GObject * gobject)173 ags_worker_thread_finalize(GObject *gobject)
174 {
175 AgsWorkerThread *worker_thread;
176
177 gboolean running;
178 gboolean do_exit;
179
180 worker_thread = AGS_WORKER_THREAD(gobject);
181
182 if(worker_thread == ags_thread_self()){
183 do_exit = TRUE;
184 }else{
185 do_exit = FALSE;
186 }
187
188 running = ags_worker_thread_test_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUNNING);
189
190 /* call parent */
191 G_OBJECT_CLASS(ags_worker_thread_parent_class)->finalize(gobject);
192
193 if(do_exit){
194 g_thread_exit(NULL);
195 }
196 }
197
198 void
ags_worker_thread_start(AgsThread * thread)199 ags_worker_thread_start(AgsThread *thread)
200 {
201 AgsWorkerThread *worker_thread;
202
203 worker_thread = AGS_WORKER_THREAD(thread);
204
205 /* */
206 AGS_THREAD_CLASS(ags_worker_thread_parent_class)->start(thread);
207
208 ags_worker_thread_set_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUNNING);
209
210 worker_thread->worker_thread = g_thread_new("Advanced Gtk+ Sequencer - worker",
211 ags_woker_thread_do_poll_loop,
212 worker_thread);
213 }
214
215 void
ags_worker_thread_run(AgsThread * thread)216 ags_worker_thread_run(AgsThread *thread)
217 {
218 AgsWorkerThread *worker_thread;
219
220 worker_thread = AGS_WORKER_THREAD(thread);
221
222 /* synchronization point */
223 if(ags_worker_thread_test_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUN_WAIT) &&
224 !ags_worker_thread_test_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUN_DONE)){
225 g_mutex_lock(&(worker_thread->run_mutex));
226
227 ags_worker_thread_set_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUN_SYNC);
228
229 while(ags_worker_thread_test_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUN_WAIT) &&
230 !ags_worker_thread_test_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUN_DONE)){
231 g_cond_wait(&(worker_thread->run_cond),
232 &(worker_thread->run_mutex));
233 }
234
235 ags_worker_thread_set_status_flags(worker_thread, (AGS_WORKER_THREAD_STATUS_RUN_WAIT |
236 AGS_WORKER_THREAD_STATUS_RUN_DONE));
237
238 g_mutex_unlock(&(worker_thread->run_mutex));
239 }
240 }
241
242 void
ags_worker_thread_stop(AgsThread * thread)243 ags_worker_thread_stop(AgsThread *thread)
244 {
245 AgsWorkerThread *worker_thread;
246
247 worker_thread = AGS_WORKER_THREAD(thread);
248
249 ags_worker_thread_unset_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUNNING);
250
251 /* call parent */
252 AGS_THREAD_CLASS(ags_worker_thread_parent_class)->stop(thread);
253 }
254
255 /**
256 * ags_worker_thread_test_status_flags:
257 * @worker_thread: the #AgsWorkerThread
258 * @status_flags: status flags
259 *
260 * Test @status_flags of @worker_thread.
261 *
262 * Returns: %TRUE if status flags set, otherwise %FALSE
263 *
264 * Since: 3.0.0
265 */
266 gboolean
ags_worker_thread_test_status_flags(AgsWorkerThread * worker_thread,guint status_flags)267 ags_worker_thread_test_status_flags(AgsWorkerThread *worker_thread, guint status_flags)
268 {
269 gboolean retval;
270
271 if(!AGS_IS_WORKER_THREAD(worker_thread)){
272 return(FALSE);
273 }
274
275 retval = ((status_flags & (g_atomic_int_get(&(worker_thread->status_flags)))) != 0) ? TRUE: FALSE;
276
277 return(retval);
278 }
279
280 /**
281 * ags_worker_thread_set_status_flags:
282 * @worker_thread: the #AgsWorkerThread
283 * @status_flags: status flags
284 *
285 * Set status flags.
286 *
287 * Since: 3.0.0
288 */
289 void
ags_worker_thread_set_status_flags(AgsWorkerThread * worker_thread,guint status_flags)290 ags_worker_thread_set_status_flags(AgsWorkerThread *worker_thread, guint status_flags)
291 {
292 if(!AGS_IS_WORKER_THREAD(worker_thread)){
293 return;
294 }
295
296 g_atomic_int_or(&(worker_thread->status_flags),
297 status_flags);
298 }
299
300 /**
301 * ags_worker_thread_unset_status_flags:
302 * @worker_thread: the #AgsWorkerThread
303 * @status_flags: status flags
304 *
305 * Unset status flags.
306 *
307 * Since: 3.0.0
308 */
309 void
ags_worker_thread_unset_status_flags(AgsWorkerThread * worker_thread,guint status_flags)310 ags_worker_thread_unset_status_flags(AgsWorkerThread *worker_thread, guint status_flags)
311 {
312 if(!AGS_IS_WORKER_THREAD(worker_thread)){
313 return;
314 }
315
316 g_atomic_int_and(&(worker_thread->status_flags),
317 (~status_flags));
318 }
319
320 /**
321 * ags_woker_thread_do_poll_loop:
322 * @ptr: the #AgsWorkerThread
323 *
324 * Do loop and invoke ags_worker_thread_do_poll() unless flag
325 * AGS_WORKER_THREAD_RUNNING was unset.
326 *
327 * Since: 3.0.0
328 */
329 void*
ags_woker_thread_do_poll_loop(void * ptr)330 ags_woker_thread_do_poll_loop(void *ptr)
331 {
332 AgsWorkerThread *worker_thread;
333
334 worker_thread = (AgsWorkerThread *) ptr;
335
336 while(ags_worker_thread_test_status_flags(worker_thread, AGS_WORKER_THREAD_STATUS_RUNNING)){
337 ags_worker_thread_do_poll(worker_thread);
338 }
339
340 g_thread_exit(NULL);
341
342 return(NULL);
343 }
344
345 /**
346 * ags_worker_thread_do_poll:
347 * @worker_thread: the #AgsWorkerThread
348 *
349 * Do poll your work. It is called of the worker thread.
350 *
351 * Since: 3.0.0
352 */
353 void
ags_worker_thread_do_poll(AgsWorkerThread * worker_thread)354 ags_worker_thread_do_poll(AgsWorkerThread *worker_thread)
355 {
356 g_return_if_fail(AGS_IS_WORKER_THREAD(worker_thread));
357 g_object_ref(worker_thread);
358 g_signal_emit(worker_thread,
359 worker_thread_signals[DO_POLL], 0);
360 g_object_unref(worker_thread);
361 }
362
363 /**
364 * ags_worker_thread_new:
365 *
366 * Create a new instance of #AgsWorkerThread.
367 *
368 * Returns: the new #AgsWorkerThread
369 *
370 * Since: 3.0.0
371 */
372 AgsWorkerThread*
ags_worker_thread_new()373 ags_worker_thread_new()
374 {
375 AgsWorkerThread *worker_thread;
376
377 worker_thread = (AgsWorkerThread *) g_object_new(AGS_TYPE_WORKER_THREAD,
378 NULL);
379
380 return(worker_thread);
381 }
382