output: using thread_cb
[platform/core/uifw/libtdm.git] / src / tdm_thread.c
1 /**************************************************************************
2  *
3  * libtdm
4  *
5  * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6  *
7  * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8  *          JinYoung Jeon <jy0.jeon@samsung.com>,
9  *          Taeheon Kim <th908.kim@samsung.com>,
10  *          YoungJun Cho <yj44.cho@samsung.com>,
11  *          SooChan Lim <sc1.lim@samsung.com>,
12  *          Boram Park <sc1.lim@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "tdm_private.h"
41
42 static tdm_private_thread *keep_private_thread;
43
44 struct _tdm_private_thread {
45         tdm_private_loop *private_loop;
46
47         pthread_cond_t event_cond;
48         pthread_t event_thread;
49
50         pid_t display_tid;
51         pid_t thread_tid;
52
53         /* 0: read, 1: write (tdm-thread -> display-thread) */
54         int pipe[2];
55
56         /* 0: read, 1: write (tdm-thread <- display-thread) */
57         int sub_pipe[2];
58         tdm_event_loop_source *sub_event_source;
59 };
60
61 typedef struct _tdm_private_thread_cb {
62         struct list_head link;
63         struct list_head call_link;
64
65         void *object;
66         tdm_thread_cb_type cb_type;
67         void *cb_data;
68         tdm_thread_cb func;
69         void *user_data;
70
71         pid_t owner_tid;
72         unsigned int called;
73 } tdm_private_thread_cb;
74
75 static tdm_thread_find_object find_funcs[TDM_THREAD_CB_MAX] = {0, };
76 static struct list_head cb_list;
77
78 static void _tdm_thread_free_cb(tdm_private_thread_cb *cb);
79
80 static tdm_error
81 _tdm_thread_handle_events(int fd, tdm_event_loop_mask mask, void *user_data)
82 {
83         tdm_private_loop *private_loop = user_data;
84         tdm_error ret;
85
86         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
87         TDM_RETURN_VAL_IF_FAIL(private_loop != NULL, TDM_ERROR_OPERATION_FAILED);
88         TDM_RETURN_VAL_IF_FAIL(private_loop->dpy != NULL, TDM_ERROR_OPERATION_FAILED);
89
90         ret = tdm_thread_handle_cb(private_loop);
91
92         TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret);
93
94         return TDM_ERROR_NONE;
95 }
96
97 static void*
98 _tdm_thread_main(void *data)
99 {
100         tdm_private_thread *private_thread = (tdm_private_thread*)data;
101         tdm_private_loop *private_loop = private_thread->private_loop;
102         int fd;
103         struct pollfd fds;
104         int ret;
105         tdm_error error;
106
107         /* Not lock/unlock for the private_thread and private_loop structure
108          * because they won't be destroyed as long as tdm thread is running.
109          * When they're destroyed, we have already exit the tdm thread.
110          */
111         private_thread->thread_tid = syscall(SYS_gettid);
112
113         TDM_INFO("display_tid:%d, thread_tid: %d",
114                          private_thread->display_tid, private_thread->thread_tid);
115
116         /* fd SHOULD be the same with sub_pipe[0] to make sure that using
117          * tdm_thread_get_fd, tdm_thread_send_cb, tdm_thread_handle_cb in
118          * both threads is fine. Otherwise, assert.
119          */
120         tdm_display_lock(private_loop->dpy);
121         assert(tdm_thread_get_fd(private_loop) == private_thread->sub_pipe[0]);
122
123         private_thread->sub_event_source =
124                 tdm_event_loop_add_fd_handler(private_loop->dpy,
125                                                                           private_thread->sub_pipe[0],
126                                                                           TDM_EVENT_LOOP_READABLE,
127                                                                           _tdm_thread_handle_events,
128                                                                           private_loop,
129                                                                           &error);
130         TDM_GOTO_IF_FAIL(error == TDM_ERROR_NONE, exit_thread);
131         TDM_GOTO_IF_FAIL(private_thread->sub_event_source != NULL, exit_thread);
132
133         pthread_cond_signal(&private_thread->event_cond);
134
135         /* mutex shall be locked by the thread calling pthread_cond_signal() */
136         tdm_display_unlock(private_loop->dpy);
137
138         fd = tdm_event_loop_get_fd(private_loop->dpy);
139         if (fd < 0) {
140                 TDM_ERR("couldn't get fd");
141                 goto exit_thread;
142         }
143
144         fds.events = POLLIN;
145         fds.fd = fd;
146         fds.revents = 0;
147
148         while (1) {
149                 if (tdm_debug_module & TDM_DEBUG_EVENT)
150                         TDM_INFO("server flush");
151                 tdm_event_loop_flush(private_loop->dpy);
152
153                 if (tdm_debug_module & TDM_DEBUG_EVENT)
154                         TDM_INFO("fd(%d) polling in", fd);
155
156                 ret = poll(&fds, 1, -1);
157
158                 if (tdm_debug_module & TDM_DEBUG_EVENT)
159                         TDM_INFO("fd(%d) polling out", fd);
160
161                 if (ret < 0) {
162                         if (errno == EINTR || errno == EAGAIN)  /* normal case */
163                                 continue;
164                         else {
165                                 TDM_ERR("poll failed: %m");
166                                 goto exit_thread;
167                         }
168                 }
169
170                 if (tdm_debug_module & TDM_DEBUG_EVENT)
171                         TDM_INFO("thread got events");
172
173                 if (tdm_event_loop_dispatch(private_loop->dpy) < 0)
174                         TDM_ERR("dispatch error");
175         }
176
177 exit_thread:
178         tdm_display_unlock(private_loop->dpy);
179         pthread_exit(NULL);
180 }
181
182 /* NOTE: tdm thread doesn't care about multi-thread. */
183 INTERN tdm_error
184 tdm_thread_init(tdm_private_loop *private_loop)
185 {
186         tdm_private_display *private_display;
187         tdm_private_thread *private_thread;
188         int thread;
189
190         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
191         TDM_RETURN_VAL_IF_FAIL(private_loop->dpy, TDM_ERROR_OPERATION_FAILED);
192
193         private_display = private_loop->dpy;
194         TDM_RETURN_VAL_IF_FAIL(private_display->private_loop, TDM_ERROR_OPERATION_FAILED);
195
196         LIST_INITHEAD(&cb_list);
197
198         if (private_loop->private_thread)
199                 return TDM_ERROR_NONE;
200
201         /* enable as default */
202         thread = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_THREAD, 1);
203         if (!thread) {
204                 TDM_INFO("not using a TDM event thread");
205                 return TDM_ERROR_NONE;
206         }
207
208         private_thread = calloc(1, sizeof * private_thread);
209         if (!private_thread) {
210                 TDM_ERR("alloc failed");
211                 return TDM_ERROR_OUT_OF_MEMORY;
212         }
213
214         if (pthread_cond_init(&private_thread->event_cond, NULL)) {
215                 TDM_ERR("pthread_cond_init failed: %m");
216                 free(private_thread);
217                 return TDM_ERROR_OUT_OF_MEMORY;
218         }
219
220         if (pipe(private_thread->pipe) != 0) {
221                 TDM_ERR("pipe failed: %m");
222                 pthread_cond_destroy(&private_thread->event_cond);
223                 free(private_thread);
224                 return TDM_ERROR_OPERATION_FAILED;
225         }
226
227         if (pipe(private_thread->sub_pipe) != 0) {
228                 TDM_ERR("sub_pipe failed: %m");
229                 close(private_thread->pipe[0]);
230                 close(private_thread->pipe[1]);
231                 pthread_cond_destroy(&private_thread->event_cond);
232                 free(private_thread);
233                 return TDM_ERROR_OPERATION_FAILED;
234         }
235
236         keep_private_thread = private_thread;
237
238         private_thread->private_loop = private_loop;
239         private_loop->private_thread = private_thread;
240
241         private_thread->display_tid = syscall(SYS_gettid);
242
243         /* pthread_cond_wait atomically release mutex, Upon successful return,
244          * the mutex shall have been locked and shall be owned by the calling thread
245          */
246         tdm_mutex_locked = 0;
247         pthread_create(&private_thread->event_thread, NULL, _tdm_thread_main,
248                                    private_thread);
249
250         /* wait until the tdm thread starts */
251         pthread_cond_wait(&private_thread->event_cond, &private_display->lock);
252         tdm_mutex_locked = 1;
253
254         TDM_INFO("using a TDM event thread. pipe(%d,%d) sub_pipe(%d,%d)",
255                          private_thread->pipe[0], private_thread->pipe[1],
256                          private_thread->sub_pipe[0], private_thread->sub_pipe[1]);
257
258         return TDM_ERROR_NONE;
259 }
260
261 INTERN void
262 tdm_thread_deinit(tdm_private_loop *private_loop)
263 {
264         tdm_private_display *private_display;
265         tdm_private_thread_cb *cb = NULL, *hh = NULL;
266         int i;
267
268         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
269
270         if (!private_loop->private_thread)
271                 return;
272
273         if (private_loop->private_thread->sub_event_source)
274                 tdm_event_loop_source_remove(private_loop->private_thread->sub_event_source);
275
276         pthread_cancel(private_loop->private_thread->event_thread);
277
278         private_display = private_loop->dpy;
279
280         /* before falling into the block of pthread_join, we have to unlock the mutex
281          * for subthread to use the mutex.
282          */
283         _pthread_mutex_unlock(&private_display->lock);
284         pthread_join(private_loop->private_thread->event_thread, NULL);
285
286         tdm_log_reset();
287
288         LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list, link) {
289                 _tdm_thread_free_cb(cb);
290         }
291
292         if (private_loop->private_thread->pipe[0] >= 0)
293                 close(private_loop->private_thread->pipe[0]);
294         if (private_loop->private_thread->pipe[1] >= 0)
295                 close(private_loop->private_thread->pipe[1]);
296
297         if (private_loop->private_thread->sub_pipe[0] >= 0)
298                 close(private_loop->private_thread->sub_pipe[0]);
299         if (private_loop->private_thread->sub_pipe[1] >= 0)
300                 close(private_loop->private_thread->sub_pipe[1]);
301
302         pthread_cond_destroy(&private_loop->private_thread->event_cond);
303
304         free(private_loop->private_thread);
305         private_loop->private_thread = NULL;
306         keep_private_thread = NULL;
307
308         for (i = 0; i < TDM_THREAD_CB_MAX; i++)
309                 find_funcs[i] = NULL;
310
311         TDM_INFO("Finish a TDM event thread");
312 }
313
314 INTERN int
315 tdm_thread_get_fd(tdm_private_loop *private_loop)
316 {
317         tdm_private_thread *private_thread;
318         int in_main;
319
320         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
321         TDM_RETURN_VAL_IF_FAIL(private_loop, -1);
322         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, -1);
323
324         private_thread = private_loop->private_thread;
325
326         /* seems like ticky. but easy way to use the same APIs for both threads */
327         in_main = tdm_thread_in_display_thread(syscall(SYS_gettid));
328
329         if (in_main)
330                 return private_thread->pipe[0];
331         else
332                 return private_thread->sub_pipe[0];
333 }
334
335 INTERN tdm_error
336 tdm_thread_send_cb(tdm_private_loop *private_loop, tdm_thread_cb_base *base)
337 {
338         tdm_private_thread *private_thread;
339         ssize_t len;
340         int pipe, in_main;
341
342         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
343         TDM_RETURN_VAL_IF_FAIL(base, TDM_ERROR_INVALID_PARAMETER);
344         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_INVALID_PARAMETER);
345         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, TDM_ERROR_INVALID_PARAMETER);
346
347         private_thread = private_loop->private_thread;
348
349         /* seems like ticky. but easy way to use the same APIs for both threads */
350         in_main = tdm_thread_in_display_thread(syscall(SYS_gettid));
351
352         if (in_main)
353                 pipe = private_thread->sub_pipe[1];
354         else
355                 pipe = private_thread->pipe[1];
356
357         if (tdm_debug_module & TDM_DEBUG_THREAD)
358                 TDM_INFO("fd(%d) type(%d), length(%d)", pipe, base->type, base->length);
359
360         len = write(pipe, base, base->length);
361         if (len != base->length) {
362                 TDM_ERR("write failed (%d != %d): %m", (int)len, base->length);
363                 return TDM_ERROR_OPERATION_FAILED;
364         }
365
366         if (tdm_debug_module & TDM_DEBUG_THREAD)
367                 TDM_INFO("[%s] write fd(%d) length(%d)", (in_main) ? "main" : "sub", pipe, len);
368
369         return TDM_ERROR_NONE;
370 }
371
372 INTERN tdm_error
373 tdm_thread_handle_cb(tdm_private_loop *private_loop)
374 {
375         tdm_private_thread *private_thread;
376         tdm_thread_cb_base *base;
377         char buffer[1024];
378         unsigned int i;
379         int len, pipe, in_main;
380
381         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
382         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_INVALID_PARAMETER);
383         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, TDM_ERROR_INVALID_PARAMETER);
384
385         private_thread = private_loop->private_thread;
386
387         /* seems like ticky. but easy way to use the same APIs for both threads */
388         in_main = tdm_thread_in_display_thread(syscall(SYS_gettid));
389
390         if (in_main)
391                 pipe = private_thread->pipe[0];
392         else
393                 pipe = private_thread->sub_pipe[0];
394
395         do {
396                 len = read(pipe, buffer, sizeof buffer);
397         } while (len < 0 && errno == EINTR);
398
399         if (tdm_debug_module & TDM_DEBUG_THREAD)
400                 TDM_INFO("[%s] read fd(%d) length(%d)", (in_main) ? "main" : "sub", pipe, len);
401
402         if (len < 0) {
403                 TDM_ERR("read failed: errno(%d), len(%d) %m", errno, len);
404                 return TDM_ERROR_OPERATION_FAILED;
405         }
406
407         if (len == 0)
408                 return TDM_ERROR_NONE;
409
410         if (len < sizeof * base) {
411                 TDM_ERR("read failed: len(%d)", len);
412                 return TDM_ERROR_OPERATION_FAILED;
413         }
414
415         i = 0;
416         while (i < len) {
417                 base = (tdm_thread_cb_base*)&buffer[i];
418                 if (tdm_debug_module & TDM_DEBUG_THREAD)
419                         TDM_INFO("type(%d), length(%d)", base->type, base->length);
420                 switch (base->type) {
421                 case TDM_THREAD_CB_OUTPUT_COMMIT:
422                 case TDM_THREAD_CB_OUTPUT_VBLANK:
423                 case TDM_THREAD_CB_OUTPUT_CHANGE:
424                 case TDM_THREAD_CB_PP_DONE:
425                 case TDM_THREAD_CB_CAPTURE_DONE:
426                 case TDM_THREAD_CB_VBLANK_SW:
427                 case TDM_THREAD_CB_VBLANK_CREATE:
428                 case TDM_THREAD_CB_NEED_VALIDATE:
429                         tdm_thread_cb_call(NULL, base);
430                         break;
431                 default:
432                         break;
433                 }
434                 i += base->length;
435         }
436
437         return TDM_ERROR_NONE;
438 }
439
440 INTERN int
441 tdm_thread_in_display_thread(pid_t tid)
442 {
443         if (!keep_private_thread)
444                 return 1;
445
446         /* DON'T check TDM_MUTEX_IS_LOCKED here */
447
448         return (keep_private_thread->display_tid == tid) ? 1 : 0;
449 }
450
451 INTERN int
452 tdm_thread_is_running(void)
453 {
454         /* DON'T check TDM_MUTEX_IS_LOCKED here */
455
456         return (keep_private_thread) ? 1 : 0;
457 }
458
459 static void
460 _tdm_thread_free_cb(tdm_private_thread_cb *cb)
461 {
462         if (tdm_debug_module & TDM_DEBUG_THREAD)
463                 TDM_INFO("cb(%p) removed", cb);
464
465         assert(LIST_IS_EMPTY(&cb->call_link));
466
467         LIST_DEL(&cb->link);
468         free(cb);
469 }
470
471 static tdm_private_thread_cb *
472 _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)
473 {
474         tdm_private_thread_cb *cb = NULL;
475
476         LIST_FOR_EACH_ENTRY(cb, &cb_list, link) {
477                 if (cb->object == object &&
478                         cb->cb_type == cb_type &&
479                         cb->cb_data == cb_data &&
480                         cb->func == func &&
481                         cb->user_data == user_data &&
482                         cb->owner_tid == owner_tid)
483                         return cb;
484         }
485
486         return NULL;
487 }
488
489 static void
490 _tdm_thread_reset_cb(tdm_thread_cb_type cb_type)
491 {
492         tdm_private_thread_cb *cb = NULL;
493
494         LIST_FOR_EACH_ENTRY(cb, &cb_list, link) {
495                 if (cb->cb_type == cb_type)
496                         cb->called = 0;
497         }
498 }
499
500 INTERN void
501 tdm_thread_cb_set_find_func(tdm_thread_cb_type cb_type, tdm_thread_find_object func)
502 {
503         TDM_RETURN_IF_FAIL(cb_type > 0);
504
505         if (func && find_funcs[cb_type])
506                 TDM_NEVER_GET_HERE();
507
508         find_funcs[cb_type] = func;
509 }
510
511 INTERN tdm_error
512 tdm_thread_cb_add(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data)
513 {
514         tdm_private_thread_cb *cb = NULL;
515         pid_t caller_tid;
516
517         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
518         TDM_RETURN_VAL_IF_FAIL(object != NULL, TDM_ERROR_INVALID_PARAMETER);
519         TDM_RETURN_VAL_IF_FAIL(cb_type > 0, TDM_ERROR_INVALID_PARAMETER);
520         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
521
522         caller_tid = syscall(SYS_gettid);
523
524         cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid);
525         if (cb) {
526                 TDM_ERR("can't be added twice with same data");
527 #if 1
528                 assert(0);
529 #endif
530                 return TDM_ERROR_BAD_REQUEST;
531         }
532
533         cb = calloc(1, sizeof *cb);
534         if (!cb) {
535                 TDM_ERR("calloc failed");
536                 return TDM_ERROR_OUT_OF_MEMORY;
537         }
538
539         LIST_ADDTAIL(&cb->link, &cb_list);
540         LIST_INITHEAD(&cb->call_link);
541
542         cb->object = object;
543         cb->cb_type = cb_type;
544         cb->cb_data = cb_data;
545         cb->func = func;
546         cb->user_data = user_data;
547         cb->owner_tid = caller_tid;
548
549         if (tdm_debug_module & TDM_DEBUG_THREAD)
550                 TDM_INFO("cb_type(%d) cb(%p) added", cb_type, cb);
551
552         return TDM_ERROR_NONE;
553 }
554
555 INTERN void
556 tdm_thread_cb_remove(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data)
557 {
558         tdm_private_thread_cb *cb;
559         pid_t caller_tid;
560
561         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
562         TDM_RETURN_IF_FAIL(object != NULL);
563         TDM_RETURN_IF_FAIL(cb_type > 0);
564         TDM_RETURN_IF_FAIL(func != NULL);
565
566         caller_tid = syscall(SYS_gettid);
567
568         cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid);
569         if (!cb)
570                 return;
571
572         _tdm_thread_free_cb(cb);
573 }
574
575 /* when call a callback, we check both cb_base's type and cb_base's data,
576  * because a callback is added with cb_type and cb_data.
577  */
578 INTERN tdm_error
579 tdm_thread_cb_call(void *object, tdm_thread_cb_base *cb_base)
580 {
581         tdm_private_display *private_display = tdm_display_get();
582         tdm_private_thread_cb *cb = NULL, *hh = NULL;
583         int handler_in_other_thread = 0;
584         pid_t caller_tid;
585         struct list_head call_list;
586         tdm_error ret;
587
588         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
589         TDM_RETURN_VAL_IF_FAIL(cb_base != NULL, TDM_ERROR_INVALID_PARAMETER);
590         TDM_RETURN_VAL_IF_FAIL(cb_base->type > 0, TDM_ERROR_INVALID_PARAMETER);
591         TDM_RETURN_VAL_IF_FAIL(cb_base->length > 0, TDM_ERROR_INVALID_PARAMETER);
592         TDM_RETURN_VAL_IF_FAIL(cb_base->sync == 0 || cb_base->sync == 1, TDM_ERROR_INVALID_PARAMETER);
593         TDM_RETURN_VAL_IF_FAIL(cb_base->object_stamp > 0, TDM_ERROR_INVALID_PARAMETER);
594
595         caller_tid = syscall(SYS_gettid);
596
597         assert(find_funcs[cb_base->type] != NULL);
598
599         if (!object) {
600                 object = find_funcs[cb_base->type](private_display, cb_base->object_stamp);
601                 if (!object) {
602                         TDM_WRN("%p gone", object);
603                         return TDM_ERROR_NONE;
604                 }
605         }
606
607         LIST_INITHEAD(&call_list);
608
609         LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list, link) {
610                 if (cb->called ||
611                         cb->object != object ||
612                         cb->cb_type != cb_base->type ||
613                         cb->cb_data != cb_base->data)
614                         continue;
615
616                 if (cb->owner_tid == caller_tid)
617                         LIST_ADDTAIL(&cb->call_link, &call_list);
618                 else
619                         handler_in_other_thread = 1;
620         }
621
622         if (!LIST_IS_EMPTY(&call_list)) {
623                 LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &call_list, call_link) {
624                         LIST_DELINIT(&cb->call_link);
625                         cb->called = 1;
626                         if (tdm_debug_module & TDM_DEBUG_THREAD)
627                                 TDM_INFO("cb_type(%d) cb(%p) called", cb->cb_type, cb);
628                         cb->func(private_display, cb->object, cb_base, cb->user_data);
629                 }
630         }
631
632         assert(LIST_IS_EMPTY(&call_list));
633
634         if (!handler_in_other_thread) {
635                 _tdm_thread_reset_cb(cb_base->type);
636                 if (keep_private_thread) {
637                         if (cb_base->sync) {
638                                 pthread_cond_signal(&keep_private_thread->event_cond);
639                                 if (tdm_debug_module & TDM_DEBUG_THREAD)
640                                         TDM_INFO("pthread broadcase");
641                         }
642                 }
643                 if (tdm_debug_module & TDM_DEBUG_THREAD)
644                         TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync);
645                 return TDM_ERROR_NONE;
646         }
647
648         /* Once we reach here, it means that keep_private_thread is not NULL.
649          * Just make the crash. Avoiding it is not going to help us.
650          */
651         ret = tdm_thread_send_cb(private_display->private_loop, cb_base);
652         TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED);
653
654         /* waiting until all cb are done in another thread */
655         if (cb_base->sync) {
656                 if (tdm_debug_module & TDM_DEBUG_THREAD)
657                         TDM_INFO("pthread wait");
658
659                 /* pthread_cond_wait atomically release mutex, Upon successful return,
660                  * the mutex shall have been locked and shall be owned by the calling thread
661                  */
662                 tdm_mutex_locked = 0;
663                 pthread_cond_wait(&keep_private_thread->event_cond, &private_display->lock);
664                 tdm_mutex_locked = 1;
665         }
666
667         if (tdm_debug_module & TDM_DEBUG_THREAD)
668                 TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync);
669
670         return TDM_ERROR_NONE;
671 }