1 #include "tpl_utils_gthread.h"
16 tpl_gthread_func init_func;
28 tpl_gsource_functions *gsource_funcs;
30 tpl_gsource_type_t type;
31 tpl_gsource *finalizer;
32 tpl_bool_t intended_destroy;
38 __gsource_remove_and_destroy(tpl_gsource *source);
41 _tpl_gthread_init(gpointer data)
43 tpl_gthread *thread = (tpl_gthread *)data;
45 g_mutex_lock(&thread->thread_mutex);
47 if (thread->init_func)
48 thread->init_func(thread->func_data);
50 g_cond_signal(&thread->thread_cond);
51 g_mutex_unlock(&thread->thread_mutex);
53 g_main_loop_run(thread->loop);
59 tpl_gthread_create(const char *thread_name,
60 tpl_gthread_func init_func, void *func_data)
62 GMainContext *context = NULL;
63 GMainLoop *loop = NULL;
64 tpl_gthread *new_thread = NULL;
66 context = g_main_context_new();
68 TPL_ERR("Failed to create GMainContext");
72 loop = g_main_loop_new(context, FALSE);
74 TPL_ERR("Failed to create GMainLoop");
75 g_main_context_unref(context);
79 // context's ref count was increased in g_main_loop_new
80 g_main_context_unref(context);
82 new_thread = calloc(1, sizeof(tpl_gthread));
84 TPL_ERR("Failed to allocate tpl_gthread");
86 // context is also destroyed when loop is destroyed.
87 g_main_loop_unref(loop);
92 g_mutex_init(&new_thread->thread_mutex);
93 g_cond_init(&new_thread->thread_cond);
95 g_mutex_init(&new_thread->pause_mutex);
96 g_mutex_init(&new_thread->idle_mutex);
97 g_cond_init(&new_thread->idle_cond);
99 new_thread->is_idle = TPL_FALSE;
100 new_thread->paused = TPL_FALSE;
102 g_mutex_lock(&new_thread->thread_mutex);
104 new_thread->loop = loop;
105 new_thread->init_func = init_func;
106 new_thread->func_data = func_data;
107 new_thread->thread = g_thread_new(thread_name,
108 _tpl_gthread_init, new_thread);
109 g_cond_wait(&new_thread->thread_cond,
110 &new_thread->thread_mutex);
112 g_mutex_unlock(&new_thread->thread_mutex);
118 tpl_gthread_destroy(tpl_gthread *thread)
120 g_mutex_lock(&thread->thread_mutex);
122 g_main_loop_quit(thread->loop);
123 g_thread_join(thread->thread);
124 g_main_loop_unref(thread->loop);
128 g_mutex_unlock(&thread->thread_mutex);
130 g_mutex_clear(&thread->pause_mutex);
131 g_mutex_clear(&thread->idle_mutex);
132 g_cond_clear(&thread->idle_cond);
134 g_mutex_clear(&thread->thread_mutex);
135 g_cond_clear(&thread->thread_cond);
137 thread->func_data = NULL;
138 thread->thread = NULL;
145 _thread_source_prepare(GSource *source, gint *time)
147 tpl_gsource *gsource = (tpl_gsource *)source;
148 tpl_bool_t ret = TPL_FALSE;
150 if (gsource->type != SOURCE_TYPE_NORMAL)
153 if (gsource->gsource_funcs && gsource->gsource_funcs->prepare)
154 ret = gsource->gsource_funcs->prepare(gsource);
162 _thread_source_check(GSource *source)
164 tpl_gsource *gsource = (tpl_gsource *)source;
165 tpl_bool_t ret = TPL_FALSE;
167 if (gsource->type != SOURCE_TYPE_NORMAL)
170 if (gsource->gsource_funcs && gsource->gsource_funcs->check)
171 ret = gsource->gsource_funcs->check(gsource);
177 _thread_source_dispatch(GSource *source, GSourceFunc cb, gpointer data)
179 tpl_gsource *gsource = (tpl_gsource *)source;
180 gboolean ret = G_SOURCE_CONTINUE;
181 GIOCondition cond = g_source_query_unix_fd(source, gsource->tag);
182 tpl_gthread *thread = gsource->thread;
186 if (cond & G_IO_IN) {
188 uint64_t message = 0;
190 if (gsource->fd_type == FD_TYPE_EVENT) {
191 s = read(gsource->fd, &message, sizeof(uint64_t));
192 if (s != sizeof(uint64_t)) {
193 TPL_ERR("Failed to read from event_fd(%d)",
198 if (gsource->gsource_funcs && gsource->gsource_funcs->dispatch)
199 ret = gsource->gsource_funcs->dispatch(gsource, message);
201 if (gsource->type == SOURCE_TYPE_FINALIZER &&
202 gsource->intended_destroy == TPL_TRUE) {
203 tpl_gsource *del_source = (tpl_gsource *)gsource->data;
204 if (!g_source_is_destroyed(&del_source->gsource)) {
205 g_mutex_lock(&thread->thread_mutex);
207 __gsource_remove_and_destroy(del_source);
208 __gsource_remove_and_destroy(gsource);
210 g_cond_signal(&thread->thread_cond);
211 g_mutex_unlock(&thread->thread_mutex);
213 return G_SOURCE_REMOVE;
218 if (cond && !(cond & G_IO_IN)) {
219 /* When some io errors occur, it is not considered as a critical error.
220 * There may be problems with the screen, but it does not affect the operation. */
221 TPL_WARN("Invalid GIOCondition occured. tpl_gsource(%p) fd(%d) cond(%d)",
222 gsource, gsource->fd, cond);
224 if (gsource->type == SOURCE_TYPE_DISPOSABLE) {
225 if (gsource->gsource_funcs && gsource->gsource_funcs->dispatch)
226 ret = gsource->gsource_funcs->dispatch(gsource, 0);
230 if (gsource->type == SOURCE_TYPE_DISPOSABLE) {
231 g_mutex_lock(&thread->thread_mutex);
232 __gsource_remove_and_destroy(gsource);
233 ret = G_SOURCE_REMOVE;
234 g_mutex_unlock(&thread->thread_mutex);
241 _thread_source_finalize(GSource *source)
243 tpl_gsource *gsource = (tpl_gsource *)source;
245 if (gsource->gsource_funcs && gsource->gsource_funcs->finalize)
246 gsource->gsource_funcs->finalize(gsource);
248 if (gsource->fd_type == FD_TYPE_EVENT ||
249 gsource->fd_type == FD_TYPE_FENCE)
253 gsource->thread = NULL;
254 gsource->gsource_funcs = NULL;
255 gsource->data = NULL;
256 gsource->finalizer = NULL;
259 static GSourceFuncs _thread_source_funcs = {
260 .prepare = _thread_source_prepare,
261 .check = _thread_source_check,
262 .dispatch = _thread_source_dispatch,
263 .finalize = _thread_source_finalize,
267 tpl_gsource_create(tpl_gthread *thread, void *data, int fd, fd_type_t fd_type,
268 tpl_gsource_functions *funcs, tpl_gsource_type_t type)
270 tpl_gsource *new_gsource = NULL;
272 new_gsource = (tpl_gsource *)g_source_new(&_thread_source_funcs,
273 sizeof(tpl_gsource));
275 TPL_ERR("Failed to create new tpl_gsource");
280 new_gsource->fd = eventfd(0, EFD_CLOEXEC);
281 if (new_gsource->fd < 0) {
282 TPL_ERR("Failed to create eventfd. errno(%d)", errno);
283 g_source_unref(&new_gsource->gsource);
287 new_gsource->fd_type = FD_TYPE_EVENT;
289 new_gsource->fd = fd;
290 new_gsource->fd_type = fd_type;
293 new_gsource->thread = thread;
294 new_gsource->gsource_funcs = funcs;
295 new_gsource->data = data;
296 new_gsource->type = type;
297 new_gsource->intended_destroy = TPL_FALSE;
299 if (new_gsource->type == SOURCE_TYPE_NORMAL) {
300 tpl_gsource *finalizer = tpl_gsource_create(thread, new_gsource, -1, FD_TYPE_NONE,
301 NULL, SOURCE_TYPE_FINALIZER);
302 new_gsource->finalizer = finalizer;
304 new_gsource->finalizer = NULL;
306 new_gsource->tag = g_source_add_unix_fd(&new_gsource->gsource,
309 g_source_attach(&new_gsource->gsource,
310 g_main_loop_get_context(thread->loop));
312 TPL_LOG_D("[GSOURCE][CREATE]", "tpl_gsource(%p) thread(%p) data(%p) fd(%d) type(%d)",
313 new_gsource, thread, data, new_gsource->fd, type);
319 __gsource_remove_and_destroy(tpl_gsource *source)
321 if (g_source_is_destroyed(&source->gsource))
324 TPL_LOG_D("[GSOURCE][DESTROY]", "tpl_gsource(%p) type(%d)",
325 source, source->type);
327 g_source_remove_unix_fd(&source->gsource, source->tag);
328 g_source_destroy(&source->gsource);
329 g_source_unref(&source->gsource);
333 tpl_gsource_destroy(tpl_gsource *source, tpl_bool_t destroy_in_thread)
335 tpl_gthread *thread = source->thread;
337 if (g_source_is_destroyed(&source->gsource)) {
338 TPL_WARN("gsource(%p) already has been destroyed.",
343 g_mutex_lock(&thread->thread_mutex);
344 if (source->type == SOURCE_TYPE_NORMAL &&
346 tpl_gsource *finalizer = source->finalizer;
348 if (destroy_in_thread) {
349 finalizer->intended_destroy = TPL_TRUE;
350 tpl_gsource_send_message(finalizer, 1);
352 __gsource_remove_and_destroy(finalizer);
353 source->finalizer = NULL;
357 if (!destroy_in_thread) {
358 if (source->fd_type == FD_TYPE_FENCE &&
359 source->type == SOURCE_TYPE_DISPOSABLE) {
360 TPL_LOG_D("[GSOURCE][DESTROY]", "tpl_gsource(%p) type(%d)",
361 source, source->type);
363 g_source_remove_unix_fd(&source->gsource, source->tag);
365 g_source_destroy(&source->gsource);
366 g_source_unref(&source->gsource);
369 __gsource_remove_and_destroy(source);
372 g_mutex_unlock(&thread->thread_mutex);
376 tpl_gsource_send_message(tpl_gsource *source, uint64_t message)
378 uint64_t value = message;
381 if (source->fd_type != FD_TYPE_EVENT) {
382 TPL_ERR("source is not using eventfd. source(%p) fd(%d)",
387 ret = write(source->fd, &value, sizeof(uint64_t));
389 TPL_ERR("failed to send devent. tpl_gsource(%p)",
395 tpl_gsource_get_data(tpl_gsource *source)
406 tpl_gsource_check_io_condition(tpl_gsource *source)
411 TPL_ERR("Invalid parameter tpl_gsource is null");
415 cond = g_source_query_unix_fd(&source->gsource, source->tag);
423 tpl_gmutex_init(tpl_gmutex *gmutex)
425 g_mutex_init(gmutex);
429 tpl_gmutex_clear(tpl_gmutex *gmutex)
431 g_mutex_clear(gmutex);
435 tpl_gmutex_lock(tpl_gmutex *gmutex)
437 g_mutex_lock(gmutex);
441 tpl_gmutex_unlock(tpl_gmutex *gmutex)
443 g_mutex_unlock(gmutex);
447 tpl_gmutex_rec_init(tpl_gmutex_rec *gmutex_rec)
449 g_rec_mutex_init(gmutex_rec);
453 tpl_gmutex_rec_clear(tpl_gmutex_rec *gmutex_rec)
455 g_rec_mutex_clear(gmutex_rec);
459 tpl_gmutex_rec_lock(tpl_gmutex_rec *gmutex_rec)
461 g_rec_mutex_lock(gmutex_rec);
465 tpl_gmutex_rec_unlock(tpl_gmutex_rec *gmutex_rec)
467 g_rec_mutex_unlock(gmutex_rec);
471 tpl_gcond_init(tpl_gcond *gcond)
477 tpl_gcond_clear(tpl_gcond *gcond)
483 tpl_gcond_wait(tpl_gcond *gcond, tpl_gmutex *gmutex)
485 g_cond_wait(gcond, gmutex);
489 tpl_gcond_timed_wait(tpl_gcond *gcond, tpl_gmutex *gmutex,
492 gint64 end_time = g_get_monotonic_time() +
493 (timeout_ms * G_TIME_SPAN_MILLISECOND);
494 if (!g_cond_wait_until(gcond, gmutex, end_time))
495 return TPL_ERROR_TIME_OUT;
497 return TPL_ERROR_NONE;
501 tpl_gcond_signal(tpl_gcond *gcond)
503 g_cond_signal(gcond);
507 _thread_idle_cb(gpointer data)
509 tpl_gthread *gthread = (tpl_gthread *)data;
511 TPL_LOG_D("[WAIT_IDLE]", "THREAD IDLE CALLBACK");
513 g_mutex_lock(>hread->idle_mutex);
514 gthread->is_idle = TPL_TRUE;
515 g_cond_broadcast(>hread->idle_cond);
516 g_mutex_unlock(>hread->idle_mutex);
518 /* If the caller thread of tpl_gthread_wait_idle locked the pause_mutex,
519 * thread will be paused here until unlock */
520 TPL_LOG_D("[THREAD_PAUSE]", "try to lock pause_mutex");
521 g_mutex_lock(>hread->pause_mutex);
522 gthread->paused = TPL_FALSE;
523 g_mutex_unlock(>hread->pause_mutex);
524 TPL_LOG_D("[THREAD_RESUME]", "thread resumes");
526 return G_SOURCE_REMOVE;
529 #define WAIT_IDLE_TIMEOUT 500
532 tpl_gthread_wait_idle(tpl_gthread *gthread)
536 tpl_result_t res = TPL_ERROR_NONE;
538 TPL_LOG_D("[WAIT_IDLE]", "BEGIN");
540 g_mutex_lock(>hread->idle_mutex);
542 tpl_gthread_add_idle(gthread, (tpl_gsource_func)_thread_idle_cb, gthread);
545 end_time = g_get_monotonic_time() +
546 (WAIT_IDLE_TIMEOUT * G_TIME_SPAN_MILLISECOND);
548 ret = g_cond_wait_until(>hread->idle_cond,
549 >hread->idle_mutex,
552 TPL_ERR("wait_idle timeout!");
553 res = TPL_ERROR_TIME_OUT;
556 } while (!gthread->is_idle);
558 gthread->is_idle = TPL_FALSE;
560 g_mutex_unlock(>hread->idle_mutex);
562 TPL_LOG_D("[WAIT_IDLE]", "END");
568 tpl_gthread_pause_in_idle(tpl_gthread *gthread)
570 TPL_CHECK_ON_NULL_RETURN_VAL(gthread, TPL_FALSE);
575 /* Assume three threads. (M, C, wl-egl-thread)
576 * C thread : already locked pause_mutex and doing their own job.
577 * M thread : call pause_in_idle and trying to lock pause_mutex.
578 * wl-egl-thread : trying to lock pause_mutex in _thread_idle_cb.
580 * When C thread calls tpl_gthread_continue and unlock pause_mutex,
581 * M thread may receive schedule and lock pause_mutex.
582 * In that case, M thread should yield to wl-egl-thread, which is
583 * paused in thread_idle_cb(). */
585 g_mutex_lock(>hread->pause_mutex);
586 if (gthread->paused) {
587 g_mutex_unlock(>hread->pause_mutex);
592 } while (++cnt <= 100);
594 res = tpl_gthread_wait_idle(gthread);
595 if (res != TPL_ERROR_NONE) {
596 TPL_ERR("Failed to wait idle. | res(%d)", res);
597 gthread->paused = TPL_FALSE;
598 g_mutex_unlock(>hread->pause_mutex);
600 gthread->paused = TPL_TRUE;
603 return gthread->paused;
607 tpl_gthread_continue(tpl_gthread *gthread)
609 TPL_CHECK_ON_NULL_RETURN(gthread);
611 if (!gthread->paused) return;
612 g_mutex_unlock(>hread->pause_mutex);
616 tpl_gthread_add_idle(tpl_gthread *gthread, tpl_gsource_func idle_cb, void *data)
618 TPL_CHECK_ON_NULL_RETURN_VAL(gthread, TPL_ERROR_INVALID_PARAMETER);
620 GSource *idle_source = g_idle_source_new();
621 if (idle_source == NULL) {
622 TPL_WARN("Failed to create and attach idle source");
623 return TPL_ERROR_INVALID_OPERATION;
626 g_source_set_priority(idle_source, G_PRIORITY_DEFAULT + 10);
627 g_source_set_callback(idle_source,
628 idle_cb, (gpointer)data,
631 g_mutex_lock(>hread->thread_mutex);
632 g_source_attach(idle_source, g_main_loop_get_context(gthread->loop));
633 g_mutex_unlock(>hread->thread_mutex);
634 g_source_unref(idle_source);
636 return TPL_ERROR_NONE;