X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Ftdm_thread.c;h=d367d4ef3113960393a0d2ccbd3cdc488a6c5cdb;hb=e1484d5a3d75564e15a09d9321eb132393d890ce;hp=e9fec4899ec196bccf03d88bec841f1532dd2c6d;hpb=37fd93539622189a546c8f00296d3e3a0ac8a9ae;p=platform%2Fcore%2Fuifw%2Flibtdm.git diff --git a/src/tdm_thread.c b/src/tdm_thread.c index e9fec48..d367d4e 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -9,7 +9,7 @@ * Taeheon Kim , * YoungJun Cho , * SooChan Lim , - * Boram Park + * Boram Park * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -46,6 +46,8 @@ struct _tdm_private_thread { pthread_cond_t event_cond; pthread_t event_thread; + unsigned int event_thread_exit; + unsigned int event_thread_joined; pid_t display_tid; pid_t thread_tid; @@ -69,11 +71,13 @@ typedef struct _tdm_private_thread_cb { void *user_data; pid_t owner_tid; - unsigned int called; } tdm_private_thread_cb; static tdm_thread_find_object find_funcs[TDM_THREAD_CB_MAX] = {0, }; -static struct list_head cb_list; + +/* 0: for display thread, 1: for tdm thread */ +static struct list_head cb_list[2]; +static pthread_mutex_t cb_list_lock = PTHREAD_MUTEX_INITIALIZER; static void _tdm_thread_free_cb(tdm_private_thread_cb *cb); @@ -150,6 +154,9 @@ _tdm_thread_main(void *data) TDM_INFO("server flush"); tdm_event_loop_flush(private_loop->dpy); + if (private_thread->event_thread_exit) + break; + if (tdm_debug_module & TDM_DEBUG_EVENT) TDM_INFO("fd(%d) polling in", fd); @@ -179,13 +186,29 @@ exit_thread: pthread_exit(NULL); } +static tdm_error +_tdm_thread_exit(tdm_private_loop *private_loop) +{ + tdm_thread_cb_base cb_base; + tdm_error ret; + + memset(&cb_base, 0, sizeof cb_base); + cb_base.type = TDM_THREAD_CB_EXIT; + cb_base.length = sizeof cb_base; + + ret = tdm_thread_send_cb(private_loop, &cb_base); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + + return ret; +} + /* NOTE: tdm thread doesn't care about multi-thread. */ INTERN tdm_error tdm_thread_init(tdm_private_loop *private_loop) { tdm_private_display *private_display; tdm_private_thread *private_thread; - int thread; + int thread, i; TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); TDM_RETURN_VAL_IF_FAIL(private_loop->dpy, TDM_ERROR_OPERATION_FAILED); @@ -193,11 +216,15 @@ tdm_thread_init(tdm_private_loop *private_loop) private_display = private_loop->dpy; TDM_RETURN_VAL_IF_FAIL(private_display->private_loop, TDM_ERROR_OPERATION_FAILED); - LIST_INITHEAD(&cb_list); - if (private_loop->private_thread) return TDM_ERROR_NONE; + for (i = 0; i < TDM_THREAD_CB_MAX; i++) + find_funcs[i] = NULL; + + LIST_INITHEAD(&cb_list[0]); + LIST_INITHEAD(&cb_list[1]); + /* enable as default */ thread = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_THREAD, 1); if (!thread) { @@ -263,17 +290,16 @@ tdm_thread_deinit(tdm_private_loop *private_loop) { tdm_private_display *private_display; tdm_private_thread_cb *cb = NULL, *hh = NULL; - int i; + tdm_error ret; TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); if (!private_loop->private_thread) return; - if (private_loop->private_thread->sub_event_source) - tdm_event_loop_source_remove(private_loop->private_thread->sub_event_source); - - pthread_cancel(private_loop->private_thread->event_thread); + ret = _tdm_thread_exit(private_loop); + if (ret != TDM_ERROR_NONE) + pthread_cancel(private_loop->private_thread->event_thread); private_display = private_loop->dpy; @@ -282,10 +308,19 @@ tdm_thread_deinit(tdm_private_loop *private_loop) */ _pthread_mutex_unlock(&private_display->lock); pthread_join(private_loop->private_thread->event_thread, NULL); + private_loop->private_thread->event_thread_joined = 1; + TDM_INFO("Joined a TDM event thread"); + pthread_mutex_unlock(&cb_list_lock); tdm_log_reset(); - LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list, link) { + if (private_loop->private_thread->sub_event_source) + tdm_event_loop_source_remove(private_loop->private_thread->sub_event_source); + + LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list[0], link) { + _tdm_thread_free_cb(cb); + } + LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list[1], link) { _tdm_thread_free_cb(cb); } @@ -305,9 +340,6 @@ tdm_thread_deinit(tdm_private_loop *private_loop) private_loop->private_thread = NULL; keep_private_thread = NULL; - for (i = 0; i < TDM_THREAD_CB_MAX; i++) - find_funcs[i] = NULL; - TDM_INFO("Finish a TDM event thread"); } @@ -355,7 +387,7 @@ tdm_thread_send_cb(tdm_private_loop *private_loop, tdm_thread_cb_base *base) pipe = private_thread->pipe[1]; if (tdm_debug_module & TDM_DEBUG_THREAD) - TDM_INFO("fd(%d) type(%d), length(%d)", pipe, base->type, base->length); + TDM_INFO("fd(%d) type(%s), length(%d)", pipe, tdm_cb_type_str(base->type), base->length); len = write(pipe, base, base->length); if (len != base->length) { @@ -377,6 +409,7 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) char buffer[1024]; unsigned int i; int len, pipe, in_main; + tdm_error ret = TDM_ERROR_NONE; TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_INVALID_PARAMETER); @@ -416,72 +449,26 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) while (i < len) { base = (tdm_thread_cb_base*)&buffer[i]; if (tdm_debug_module & TDM_DEBUG_THREAD) - TDM_INFO("type(%d), length(%d)", base->type, base->length); + TDM_INFO("type(%s), length(%d)", tdm_cb_type_str(base->type), base->length); switch (base->type) { - case TDM_THREAD_CB_OUTPUT_COMMIT: { - tdm_thread_cb_output_commit *output_commit = (tdm_thread_cb_output_commit*)base; - tdm_output *output_backend = - tdm_display_find_output_stamp(private_loop->dpy, output_commit->output_stamp); - if (!output_backend) { - TDM_WRN("no output(%f)", output_commit->output_stamp); - break; - } - tdm_output_cb_commit(output_backend, output_commit->sequence, - output_commit->tv_sec, output_commit->tv_usec, - output_commit->user_data); - break; - } - case TDM_THREAD_CB_OUTPUT_VBLANK: { - tdm_thread_cb_output_vblank *output_vblank = (tdm_thread_cb_output_vblank*)base; - tdm_output *output_backend = - tdm_display_find_output_stamp(private_loop->dpy, output_vblank->output_stamp); - if (!output_backend) { - TDM_WRN("no output(%f)", output_vblank->output_stamp); - break; - } - tdm_output_cb_vblank(output_backend, output_vblank->sequence, - output_vblank->tv_sec, output_vblank->tv_usec, - output_vblank->user_data); - break; - } - case TDM_THREAD_CB_OUTPUT_STATUS: { - /* LCOV_EXCL_START */ - tdm_thread_cb_output_status *output_status = (tdm_thread_cb_output_status*)base; - tdm_output *output_backend = - tdm_display_find_output_stamp(private_loop->dpy, output_status->output_stamp); - if (!output_backend) { - TDM_WRN("no output(%f)", output_status->output_stamp); - break; - } - tdm_output_cb_status(output_backend, output_status->status, - output_status->user_data); - break; - /* LCOV_EXCL_STOP */ - } - case TDM_THREAD_CB_OUTPUT_DPMS: { - /* LCOV_EXCL_START */ - tdm_thread_cb_output_dpms *output_dpms = (tdm_thread_cb_output_dpms*)base; - tdm_output *output_backend = - tdm_display_find_output_stamp(private_loop->dpy, output_dpms->output_stamp); - if (!output_backend) { - TDM_WRN("no output(%f)", output_dpms->output_stamp); - break; - } - tdm_output_cb_dpms(output_backend, output_dpms->dpms, output_dpms->user_data); - break; - /* LCOV_EXCL_STOP */ - } + case TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE: + case TDM_THREAD_CB_OUTPUT_DESTROY: + case TDM_THREAD_CB_OUTPUT_COMMIT: + case TDM_THREAD_CB_OUTPUT_VBLANK: + case TDM_THREAD_CB_OUTPUT_STATUS: + case TDM_THREAD_CB_OUTPUT_DPMS: case TDM_THREAD_CB_PP_DONE: case TDM_THREAD_CB_CAPTURE_DONE: case TDM_THREAD_CB_VBLANK_SW: case TDM_THREAD_CB_VBLANK_CREATE: - tdm_thread_cb_call(NULL, base); + case TDM_THREAD_CB_HWC_COMMIT: + /* this event comes from other thread. so we don't need to propagate this to other thread */ + ret = tdm_thread_cb_call(NULL, base, 0); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); break; - case TDM_THREAD_CB_NEED_VALIDATE: { - tdm_thread_cb_need_validate *ev = (tdm_thread_cb_need_validate*)base; - tdm_output_cb_need_validate(ev->o); + case TDM_THREAD_CB_EXIT: + private_thread->event_thread_exit = 1; break; - } default: break; } @@ -507,7 +494,7 @@ tdm_thread_is_running(void) { /* DON'T check TDM_MUTEX_IS_LOCKED here */ - return (keep_private_thread) ? 1 : 0; + return (keep_private_thread && !keep_private_thread->event_thread_joined) ? 1 : 0; } static void @@ -523,34 +510,23 @@ _tdm_thread_free_cb(tdm_private_thread_cb *cb) } static tdm_private_thread_cb * -_tdm_thread_find_cb(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data, pid_t owner_tid) +_tdm_thread_find_cb(struct list_head *list, void *object, tdm_thread_cb_type cb_type, + void *cb_data, tdm_thread_cb func, void *user_data) { tdm_private_thread_cb *cb = NULL; - LIST_FOR_EACH_ENTRY(cb, &cb_list, link) { + LIST_FOR_EACH_ENTRY(cb, list, link) { if (cb->object == object && cb->cb_type == cb_type && cb->cb_data == cb_data && cb->func == func && - cb->user_data == user_data && - cb->owner_tid == owner_tid) + cb->user_data == user_data) return cb; } return NULL; } -static void -_tdm_thread_reset_cb(tdm_thread_cb_type cb_type) -{ - tdm_private_thread_cb *cb = NULL; - - LIST_FOR_EACH_ENTRY(cb, &cb_list, link) { - if (cb->cb_type == cb_type) - cb->called = 0; - } -} - INTERN void tdm_thread_cb_set_find_func(tdm_thread_cb_type cb_type, tdm_thread_find_object func) { @@ -567,6 +543,7 @@ tdm_thread_cb_add(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_t { tdm_private_thread_cb *cb = NULL; pid_t caller_tid; + struct list_head *list; TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); TDM_RETURN_VAL_IF_FAIL(object != NULL, TDM_ERROR_INVALID_PARAMETER); @@ -575,22 +552,28 @@ tdm_thread_cb_add(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_t caller_tid = syscall(SYS_gettid); - cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid); + pthread_mutex_lock(&cb_list_lock); + + if (tdm_thread_in_display_thread(caller_tid)) + list = &cb_list[0]; + else + list = &cb_list[1]; + + cb = _tdm_thread_find_cb(list, object, cb_type, cb_data, func, user_data); if (cb) { + pthread_mutex_unlock(&cb_list_lock); TDM_ERR("can't be added twice with same data"); -#if 1 - assert(0); -#endif return TDM_ERROR_BAD_REQUEST; } cb = calloc(1, sizeof *cb); if (!cb) { + pthread_mutex_unlock(&cb_list_lock); TDM_ERR("calloc failed"); return TDM_ERROR_OUT_OF_MEMORY; } - LIST_ADDTAIL(&cb->link, &cb_list); + LIST_ADDTAIL(&cb->link, list); LIST_INITHEAD(&cb->call_link); cb->object = object; @@ -601,7 +584,9 @@ tdm_thread_cb_add(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_t cb->owner_tid = caller_tid; if (tdm_debug_module & TDM_DEBUG_THREAD) - TDM_INFO("cb_type(%d) cb(%p) added", cb_type, cb); + TDM_INFO("cb_type(%s) cb(%p) added", tdm_cb_type_str(cb_type), cb); + + pthread_mutex_unlock(&cb_list_lock); return TDM_ERROR_NONE; } @@ -610,46 +595,65 @@ INTERN void tdm_thread_cb_remove(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data) { tdm_private_thread_cb *cb; - pid_t caller_tid; + struct list_head *list; TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); TDM_RETURN_IF_FAIL(object != NULL); TDM_RETURN_IF_FAIL(cb_type > 0); TDM_RETURN_IF_FAIL(func != NULL); - caller_tid = syscall(SYS_gettid); + pthread_mutex_lock(&cb_list_lock); - cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid); - if (!cb) - return; + list = &cb_list[0]; + cb = _tdm_thread_find_cb(list, object, cb_type, cb_data, func, user_data); + if (cb) + _tdm_thread_free_cb(cb); - _tdm_thread_free_cb(cb); + list = &cb_list[1]; + cb = _tdm_thread_find_cb(list, object, cb_type, cb_data, func, user_data); + if (cb) + _tdm_thread_free_cb(cb); + + pthread_mutex_unlock(&cb_list_lock); } /* when call a callback, we check both cb_base's type and cb_base's data, * because a callback is added with cb_type and cb_data. */ INTERN tdm_error -tdm_thread_cb_call(void *object, tdm_thread_cb_base *cb_base) +tdm_thread_cb_call(void *object, tdm_thread_cb_base *cb_base, unsigned int propagation) { tdm_private_display *private_display = tdm_display_get(); tdm_private_thread_cb *cb = NULL, *hh = NULL; int handler_in_other_thread = 0; pid_t caller_tid; + struct list_head *list, *other_list; struct list_head call_list; + static pid_t waiting_tid = 0; tdm_error ret; TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); TDM_RETURN_VAL_IF_FAIL(cb_base != NULL, TDM_ERROR_INVALID_PARAMETER); TDM_RETURN_VAL_IF_FAIL(cb_base->type > 0, TDM_ERROR_INVALID_PARAMETER); TDM_RETURN_VAL_IF_FAIL(cb_base->length > 0, TDM_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(cb_base->sync == 0 || cb_base->sync == 1, TDM_ERROR_INVALID_PARAMETER); TDM_RETURN_VAL_IF_FAIL(cb_base->object_stamp > 0, TDM_ERROR_INVALID_PARAMETER); caller_tid = syscall(SYS_gettid); assert(find_funcs[cb_base->type] != NULL); + if (keep_private_thread && keep_private_thread->thread_tid != caller_tid) { + /* A sync-type event from display-thread to tdm-thread can't be handled. + * If sync-type events happen in both threads at the same time, + * it would make a deadlock issue. + */ + assert(cb_base->sync != 1); + } + + if (tdm_debug_module & TDM_DEBUG_THREAD) + TDM_INFO("'%s' thread_cb (sync:%d, propagation:%d) ------", + tdm_cb_type_str(cb_base->type), cb_base->sync, propagation); + if (!object) { object = find_funcs[cb_base->type](private_display, cb_base->object_stamp); if (!object) { @@ -658,55 +662,99 @@ tdm_thread_cb_call(void *object, tdm_thread_cb_base *cb_base) } } + pthread_mutex_lock(&cb_list_lock); + + if (tdm_thread_in_display_thread(caller_tid)) { + list = &cb_list[0]; + other_list = &cb_list[1]; + } else { + other_list = &cb_list[0]; + list = &cb_list[1]; + } + LIST_INITHEAD(&call_list); - LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list, link) { - if (cb->called || - cb->object != object || + LIST_FOR_EACH_ENTRY_SAFE(cb, hh, list, link) { + if (cb->object != object || cb->cb_type != cb_base->type || cb->cb_data != cb_base->data) continue; - if (cb->owner_tid == caller_tid) - LIST_ADDTAIL(&cb->call_link, &call_list); - else - handler_in_other_thread = 1; + LIST_ADDTAIL(&cb->call_link, &call_list); } if (!LIST_IS_EMPTY(&call_list)) { LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &call_list, call_link) { LIST_DELINIT(&cb->call_link); - cb->called = 1; if (tdm_debug_module & TDM_DEBUG_THREAD) - TDM_INFO("cb_type(%d) cb(%p) called", cb->cb_type, cb); + TDM_INFO("cb_type(%s) cb(%p) calling", tdm_cb_type_str(cb->cb_type), cb); + pthread_mutex_unlock(&cb_list_lock); cb->func(private_display, cb->object, cb_base, cb->user_data); + pthread_mutex_lock(&cb_list_lock); } } - assert(LIST_IS_EMPTY(&call_list)); + pthread_mutex_unlock(&cb_list_lock); + + if (propagation) { + LIST_FOR_EACH_ENTRY_SAFE(cb, hh, other_list, link) { + if (cb->object != object || + cb->cb_type != cb_base->type || + cb->cb_data != cb_base->data) + continue; + + handler_in_other_thread = 1; + break; + } + } if (!handler_in_other_thread) { - _tdm_thread_reset_cb(cb_base->type); if (keep_private_thread) { - if (cb_base->sync) { + /* NOTE quick fix + * In case of 'cb_base->sync = 0' and 'waiting_tid != 0', + * probably it means one thread is waiting for signal of + * pthread condition. + */ + if (!cb_base->sync && waiting_tid != 0) { + waiting_tid = 0; pthread_cond_signal(&keep_private_thread->event_cond); if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("pthread broadcase"); } } if (tdm_debug_module & TDM_DEBUG_THREAD) - TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync); + TDM_INFO("'%s' thread_cb (sync:%d, propagation:%d) ------...", + tdm_cb_type_str(cb_base->type), cb_base->sync, propagation); return TDM_ERROR_NONE; } /* Once we reach here, it means that keep_private_thread is not NULL. * Just make the crash. Avoiding it is not going to help us. */ - ret = tdm_thread_send_cb(private_display->private_loop, cb_base); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED); + assert(keep_private_thread != NULL); + if (!cb_base->sync) { + ret = tdm_thread_send_cb(private_display->private_loop, cb_base); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED); + } /* waiting until all cb are done in another thread */ - if (cb_base->sync) { + else { + /* NOTE quick fix + * Sync type event from display-thread(main thread) can't be + * handled. In this way, if we call tdm_thread_send_cb() with + * 'cb_base->sync = 1', then libtdm will eveventually raise + * abort(). Please see commit '4abfab36' for more detail. + */ + cb_base->sync = 0; + ret = tdm_thread_send_cb(private_display->private_loop, cb_base); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED); + cb_base->sync = 1; + + /* if waiting_tid is not 0, it means there are two sync-type events at the same time. + * and it would make deadlock issue. + */ + assert(waiting_tid == 0); + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("pthread wait"); @@ -714,12 +762,14 @@ tdm_thread_cb_call(void *object, tdm_thread_cb_base *cb_base) * the mutex shall have been locked and shall be owned by the calling thread */ tdm_mutex_locked = 0; + waiting_tid = caller_tid; pthread_cond_wait(&keep_private_thread->event_cond, &private_display->lock); tdm_mutex_locked = 1; } if (tdm_debug_module & TDM_DEBUG_THREAD) - TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync); + TDM_INFO("'%s' thread_cb (sync:%d, propagation:%d) ------...", + tdm_cb_type_str(cb_base->type), cb_base->sync, propagation); return TDM_ERROR_NONE; }