thread: reset find_funcs table when init, deinit
[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, i;
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         for (i = 0; i < TDM_THREAD_CB_MAX; i++)
197                 find_funcs[i] = NULL;
198
199         LIST_INITHEAD(&cb_list);
200
201         if (private_loop->private_thread)
202                 return TDM_ERROR_NONE;
203
204         /* enable as default */
205         thread = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_THREAD, 1);
206         if (!thread) {
207                 TDM_INFO("not using a TDM event thread");
208                 return TDM_ERROR_NONE;
209         }
210
211         private_thread = calloc(1, sizeof * private_thread);
212         if (!private_thread) {
213                 TDM_ERR("alloc failed");
214                 return TDM_ERROR_OUT_OF_MEMORY;
215         }
216
217         if (pthread_cond_init(&private_thread->event_cond, NULL)) {
218                 TDM_ERR("pthread_cond_init failed: %m");
219                 free(private_thread);
220                 return TDM_ERROR_OUT_OF_MEMORY;
221         }
222
223         if (pipe(private_thread->pipe) != 0) {
224                 TDM_ERR("pipe failed: %m");
225                 pthread_cond_destroy(&private_thread->event_cond);
226                 free(private_thread);
227                 return TDM_ERROR_OPERATION_FAILED;
228         }
229
230         if (pipe(private_thread->sub_pipe) != 0) {
231                 TDM_ERR("sub_pipe failed: %m");
232                 close(private_thread->pipe[0]);
233                 close(private_thread->pipe[1]);
234                 pthread_cond_destroy(&private_thread->event_cond);
235                 free(private_thread);
236                 return TDM_ERROR_OPERATION_FAILED;
237         }
238
239         keep_private_thread = private_thread;
240
241         private_thread->private_loop = private_loop;
242         private_loop->private_thread = private_thread;
243
244         private_thread->display_tid = syscall(SYS_gettid);
245
246         /* pthread_cond_wait atomically release mutex, Upon successful return,
247          * the mutex shall have been locked and shall be owned by the calling thread
248          */
249         tdm_mutex_locked = 0;
250         pthread_create(&private_thread->event_thread, NULL, _tdm_thread_main,
251                                    private_thread);
252
253         /* wait until the tdm thread starts */
254         pthread_cond_wait(&private_thread->event_cond, &private_display->lock);
255         tdm_mutex_locked = 1;
256
257         TDM_INFO("using a TDM event thread. pipe(%d,%d) sub_pipe(%d,%d)",
258                          private_thread->pipe[0], private_thread->pipe[1],
259                          private_thread->sub_pipe[0], private_thread->sub_pipe[1]);
260
261         return TDM_ERROR_NONE;
262 }
263
264 INTERN void
265 tdm_thread_deinit(tdm_private_loop *private_loop)
266 {
267         tdm_private_display *private_display;
268         tdm_private_thread_cb *cb = NULL, *hh = NULL;
269         int i;
270
271         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
272
273         for (i = 0; i < TDM_THREAD_CB_MAX; i++)
274                 find_funcs[i] = NULL;
275
276         if (!private_loop->private_thread)
277                 return;
278
279         if (private_loop->private_thread->sub_event_source)
280                 tdm_event_loop_source_remove(private_loop->private_thread->sub_event_source);
281
282         pthread_cancel(private_loop->private_thread->event_thread);
283
284         private_display = private_loop->dpy;
285
286         /* before falling into the block of pthread_join, we have to unlock the mutex
287          * for subthread to use the mutex.
288          */
289         _pthread_mutex_unlock(&private_display->lock);
290         pthread_join(private_loop->private_thread->event_thread, NULL);
291
292         tdm_log_reset();
293
294         LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list, link) {
295                 _tdm_thread_free_cb(cb);
296         }
297
298         if (private_loop->private_thread->pipe[0] >= 0)
299                 close(private_loop->private_thread->pipe[0]);
300         if (private_loop->private_thread->pipe[1] >= 0)
301                 close(private_loop->private_thread->pipe[1]);
302
303         if (private_loop->private_thread->sub_pipe[0] >= 0)
304                 close(private_loop->private_thread->sub_pipe[0]);
305         if (private_loop->private_thread->sub_pipe[1] >= 0)
306                 close(private_loop->private_thread->sub_pipe[1]);
307
308         pthread_cond_destroy(&private_loop->private_thread->event_cond);
309
310         free(private_loop->private_thread);
311         private_loop->private_thread = NULL;
312         keep_private_thread = NULL;
313
314         TDM_INFO("Finish a TDM event thread");
315 }
316
317 INTERN int
318 tdm_thread_get_fd(tdm_private_loop *private_loop)
319 {
320         tdm_private_thread *private_thread;
321         int in_main;
322
323         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
324         TDM_RETURN_VAL_IF_FAIL(private_loop, -1);
325         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, -1);
326
327         private_thread = private_loop->private_thread;
328
329         /* seems like ticky. but easy way to use the same APIs for both threads */
330         in_main = tdm_thread_in_display_thread(syscall(SYS_gettid));
331
332         if (in_main)
333                 return private_thread->pipe[0];
334         else
335                 return private_thread->sub_pipe[0];
336 }
337
338 INTERN tdm_error
339 tdm_thread_send_cb(tdm_private_loop *private_loop, tdm_thread_cb_base *base)
340 {
341         tdm_private_thread *private_thread;
342         ssize_t len;
343         int pipe, in_main;
344
345         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
346         TDM_RETURN_VAL_IF_FAIL(base, TDM_ERROR_INVALID_PARAMETER);
347         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_INVALID_PARAMETER);
348         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, TDM_ERROR_INVALID_PARAMETER);
349
350         private_thread = private_loop->private_thread;
351
352         /* seems like ticky. but easy way to use the same APIs for both threads */
353         in_main = tdm_thread_in_display_thread(syscall(SYS_gettid));
354
355         if (in_main)
356                 pipe = private_thread->sub_pipe[1];
357         else
358                 pipe = private_thread->pipe[1];
359
360         if (tdm_debug_module & TDM_DEBUG_THREAD)
361                 TDM_INFO("fd(%d) type(%d), length(%d)", pipe, base->type, base->length);
362
363         len = write(pipe, base, base->length);
364         if (len != base->length) {
365                 TDM_ERR("write failed (%d != %d): %m", (int)len, base->length);
366                 return TDM_ERROR_OPERATION_FAILED;
367         }
368
369         if (tdm_debug_module & TDM_DEBUG_THREAD)
370                 TDM_INFO("[%s] write fd(%d) length(%d)", (in_main) ? "main" : "sub", pipe, len);
371
372         return TDM_ERROR_NONE;
373 }
374
375 INTERN tdm_error
376 tdm_thread_handle_cb(tdm_private_loop *private_loop)
377 {
378         tdm_private_thread *private_thread;
379         tdm_thread_cb_base *base;
380         char buffer[1024];
381         unsigned int i;
382         int len, pipe, in_main;
383
384         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
385         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_INVALID_PARAMETER);
386         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, TDM_ERROR_INVALID_PARAMETER);
387
388         private_thread = private_loop->private_thread;
389
390         /* seems like ticky. but easy way to use the same APIs for both threads */
391         in_main = tdm_thread_in_display_thread(syscall(SYS_gettid));
392
393         if (in_main)
394                 pipe = private_thread->pipe[0];
395         else
396                 pipe = private_thread->sub_pipe[0];
397
398         do {
399                 len = read(pipe, buffer, sizeof buffer);
400         } while (len < 0 && errno == EINTR);
401
402         if (tdm_debug_module & TDM_DEBUG_THREAD)
403                 TDM_INFO("[%s] read fd(%d) length(%d)", (in_main) ? "main" : "sub", pipe, len);
404
405         if (len < 0) {
406                 TDM_ERR("read failed: errno(%d), len(%d) %m", errno, len);
407                 return TDM_ERROR_OPERATION_FAILED;
408         }
409
410         if (len == 0)
411                 return TDM_ERROR_NONE;
412
413         if (len < sizeof * base) {
414                 TDM_ERR("read failed: len(%d)", len);
415                 return TDM_ERROR_OPERATION_FAILED;
416         }
417
418         i = 0;
419         while (i < len) {
420                 base = (tdm_thread_cb_base*)&buffer[i];
421                 if (tdm_debug_module & TDM_DEBUG_THREAD)
422                         TDM_INFO("type(%d), length(%d)", base->type, base->length);
423                 switch (base->type) {
424                 case TDM_THREAD_CB_OUTPUT_COMMIT:
425                 case TDM_THREAD_CB_OUTPUT_VBLANK:
426                 case TDM_THREAD_CB_OUTPUT_CHANGE:
427                 case TDM_THREAD_CB_PP_DONE:
428                 case TDM_THREAD_CB_CAPTURE_DONE:
429                 case TDM_THREAD_CB_VBLANK_SW:
430                 case TDM_THREAD_CB_VBLANK_CREATE:
431                 case TDM_THREAD_CB_NEED_VALIDATE:
432                         tdm_thread_cb_call(NULL, base);
433                         break;
434                 default:
435                         break;
436                 }
437                 i += base->length;
438         }
439
440         return TDM_ERROR_NONE;
441 }
442
443 INTERN int
444 tdm_thread_in_display_thread(pid_t tid)
445 {
446         if (!keep_private_thread)
447                 return 1;
448
449         /* DON'T check TDM_MUTEX_IS_LOCKED here */
450
451         return (keep_private_thread->display_tid == tid) ? 1 : 0;
452 }
453
454 INTERN int
455 tdm_thread_is_running(void)
456 {
457         /* DON'T check TDM_MUTEX_IS_LOCKED here */
458
459         return (keep_private_thread) ? 1 : 0;
460 }
461
462 static void
463 _tdm_thread_free_cb(tdm_private_thread_cb *cb)
464 {
465         if (tdm_debug_module & TDM_DEBUG_THREAD)
466                 TDM_INFO("cb(%p) removed", cb);
467
468         assert(LIST_IS_EMPTY(&cb->call_link));
469
470         LIST_DEL(&cb->link);
471         free(cb);
472 }
473
474 static tdm_private_thread_cb *
475 _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)
476 {
477         tdm_private_thread_cb *cb = NULL;
478
479         LIST_FOR_EACH_ENTRY(cb, &cb_list, link) {
480                 if (cb->object == object &&
481                         cb->cb_type == cb_type &&
482                         cb->cb_data == cb_data &&
483                         cb->func == func &&
484                         cb->user_data == user_data &&
485                         cb->owner_tid == owner_tid)
486                         return cb;
487         }
488
489         return NULL;
490 }
491
492 static void
493 _tdm_thread_reset_cb(tdm_thread_cb_type cb_type)
494 {
495         tdm_private_thread_cb *cb = NULL;
496
497         LIST_FOR_EACH_ENTRY(cb, &cb_list, link) {
498                 if (cb->cb_type == cb_type)
499                         cb->called = 0;
500         }
501 }
502
503 INTERN void
504 tdm_thread_cb_set_find_func(tdm_thread_cb_type cb_type, tdm_thread_find_object func)
505 {
506         TDM_RETURN_IF_FAIL(cb_type > 0);
507
508         if (func && find_funcs[cb_type])
509                 TDM_NEVER_GET_HERE();
510
511         find_funcs[cb_type] = func;
512 }
513
514 INTERN tdm_error
515 tdm_thread_cb_add(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data)
516 {
517         tdm_private_thread_cb *cb = NULL;
518         pid_t caller_tid;
519
520         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
521         TDM_RETURN_VAL_IF_FAIL(object != NULL, TDM_ERROR_INVALID_PARAMETER);
522         TDM_RETURN_VAL_IF_FAIL(cb_type > 0, TDM_ERROR_INVALID_PARAMETER);
523         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
524
525         caller_tid = syscall(SYS_gettid);
526
527         cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid);
528         if (cb) {
529                 TDM_ERR("can't be added twice with same data");
530 #if 1
531                 assert(0);
532 #endif
533                 return TDM_ERROR_BAD_REQUEST;
534         }
535
536         cb = calloc(1, sizeof *cb);
537         if (!cb) {
538                 TDM_ERR("calloc failed");
539                 return TDM_ERROR_OUT_OF_MEMORY;
540         }
541
542         LIST_ADDTAIL(&cb->link, &cb_list);
543         LIST_INITHEAD(&cb->call_link);
544
545         cb->object = object;
546         cb->cb_type = cb_type;
547         cb->cb_data = cb_data;
548         cb->func = func;
549         cb->user_data = user_data;
550         cb->owner_tid = caller_tid;
551
552         if (tdm_debug_module & TDM_DEBUG_THREAD)
553                 TDM_INFO("cb_type(%d) cb(%p) added", cb_type, cb);
554
555         return TDM_ERROR_NONE;
556 }
557
558 INTERN void
559 tdm_thread_cb_remove(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data)
560 {
561         tdm_private_thread_cb *cb;
562         pid_t caller_tid;
563
564         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
565         TDM_RETURN_IF_FAIL(object != NULL);
566         TDM_RETURN_IF_FAIL(cb_type > 0);
567         TDM_RETURN_IF_FAIL(func != NULL);
568
569         caller_tid = syscall(SYS_gettid);
570
571         cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid);
572         if (!cb)
573                 return;
574
575         _tdm_thread_free_cb(cb);
576 }
577
578 /* when call a callback, we check both cb_base's type and cb_base's data,
579  * because a callback is added with cb_type and cb_data.
580  */
581 INTERN tdm_error
582 tdm_thread_cb_call(void *object, tdm_thread_cb_base *cb_base)
583 {
584         tdm_private_display *private_display = tdm_display_get();
585         tdm_private_thread_cb *cb = NULL, *hh = NULL;
586         int handler_in_other_thread = 0;
587         pid_t caller_tid;
588         struct list_head call_list;
589         tdm_error ret;
590
591         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
592         TDM_RETURN_VAL_IF_FAIL(cb_base != NULL, TDM_ERROR_INVALID_PARAMETER);
593         TDM_RETURN_VAL_IF_FAIL(cb_base->type > 0, TDM_ERROR_INVALID_PARAMETER);
594         TDM_RETURN_VAL_IF_FAIL(cb_base->length > 0, TDM_ERROR_INVALID_PARAMETER);
595         TDM_RETURN_VAL_IF_FAIL(cb_base->sync == 0 || cb_base->sync == 1, TDM_ERROR_INVALID_PARAMETER);
596         TDM_RETURN_VAL_IF_FAIL(cb_base->object_stamp > 0, TDM_ERROR_INVALID_PARAMETER);
597
598         caller_tid = syscall(SYS_gettid);
599
600         assert(find_funcs[cb_base->type] != NULL);
601
602         if (!object) {
603                 object = find_funcs[cb_base->type](private_display, cb_base->object_stamp);
604                 if (!object) {
605                         TDM_WRN("%p gone", object);
606                         return TDM_ERROR_NONE;
607                 }
608         }
609
610         LIST_INITHEAD(&call_list);
611
612         LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list, link) {
613                 if (cb->called ||
614                         cb->object != object ||
615                         cb->cb_type != cb_base->type ||
616                         cb->cb_data != cb_base->data)
617                         continue;
618
619                 if (cb->owner_tid == caller_tid)
620                         LIST_ADDTAIL(&cb->call_link, &call_list);
621                 else
622                         handler_in_other_thread = 1;
623         }
624
625         if (!LIST_IS_EMPTY(&call_list)) {
626                 LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &call_list, call_link) {
627                         LIST_DELINIT(&cb->call_link);
628                         cb->called = 1;
629                         if (tdm_debug_module & TDM_DEBUG_THREAD)
630                                 TDM_INFO("cb_type(%d) cb(%p) called", cb->cb_type, cb);
631                         cb->func(private_display, cb->object, cb_base, cb->user_data);
632                 }
633         }
634
635         assert(LIST_IS_EMPTY(&call_list));
636
637         if (!handler_in_other_thread) {
638                 _tdm_thread_reset_cb(cb_base->type);
639                 if (keep_private_thread) {
640                         if (cb_base->sync) {
641                                 pthread_cond_signal(&keep_private_thread->event_cond);
642                                 if (tdm_debug_module & TDM_DEBUG_THREAD)
643                                         TDM_INFO("pthread broadcase");
644                         }
645                 }
646                 if (tdm_debug_module & TDM_DEBUG_THREAD)
647                         TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync);
648                 return TDM_ERROR_NONE;
649         }
650
651         /* Once we reach here, it means that keep_private_thread is not NULL.
652          * Just make the crash. Avoiding it is not going to help us.
653          */
654         ret = tdm_thread_send_cb(private_display->private_loop, cb_base);
655         TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED);
656
657         /* waiting until all cb are done in another thread */
658         if (cb_base->sync) {
659                 if (tdm_debug_module & TDM_DEBUG_THREAD)
660                         TDM_INFO("pthread wait");
661
662                 /* pthread_cond_wait atomically release mutex, Upon successful return,
663                  * the mutex shall have been locked and shall be owned by the calling thread
664                  */
665                 tdm_mutex_locked = 0;
666                 pthread_cond_wait(&keep_private_thread->event_cond, &private_display->lock);
667                 tdm_mutex_locked = 1;
668         }
669
670         if (tdm_debug_module & TDM_DEBUG_THREAD)
671                 TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync);
672
673         return TDM_ERROR_NONE;
674 }