thread: support cb for multi-thread communication
[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                         tdm_thread_cb_output_commit *output_commit = (tdm_thread_cb_output_commit*)base;
423                         tdm_output *output_backend =
424                                 tdm_display_find_output_stamp(private_loop->dpy, output_commit->output_stamp);
425                         if (!output_backend) {
426                                 TDM_WRN("no output(%f)", output_commit->output_stamp);
427                                 break;
428                         }
429                         tdm_output_cb_commit(output_backend, output_commit->sequence,
430                                                                  output_commit->tv_sec, output_commit->tv_usec,
431                                                                  output_commit->user_data);
432                         break;
433                 }
434                 case TDM_THREAD_CB_OUTPUT_VBLANK: {
435                         tdm_thread_cb_output_vblank *output_vblank = (tdm_thread_cb_output_vblank*)base;
436                         tdm_output *output_backend =
437                                 tdm_display_find_output_stamp(private_loop->dpy, output_vblank->output_stamp);
438                         if (!output_backend) {
439                                 TDM_WRN("no output(%f)", output_vblank->output_stamp);
440                                 break;
441                         }
442                         tdm_output_cb_vblank(output_backend, output_vblank->sequence,
443                                                                  output_vblank->tv_sec, output_vblank->tv_usec,
444                                                                  output_vblank->user_data);
445                         break;
446                 }
447                 case TDM_THREAD_CB_OUTPUT_STATUS: {
448                         /* LCOV_EXCL_START */
449                         tdm_thread_cb_output_status *output_status = (tdm_thread_cb_output_status*)base;
450                         tdm_output *output_backend =
451                                 tdm_display_find_output_stamp(private_loop->dpy, output_status->output_stamp);
452                         if (!output_backend) {
453                                 TDM_WRN("no output(%f)", output_status->output_stamp);
454                                 break;
455                         }
456                         tdm_output_cb_status(output_backend, output_status->status,
457                                                                  output_status->user_data);
458                         break;
459                         /* LCOV_EXCL_STOP */
460                 }
461                 case TDM_THREAD_CB_OUTPUT_DPMS: {
462                         /* LCOV_EXCL_START */
463                         tdm_thread_cb_output_dpms *output_dpms = (tdm_thread_cb_output_dpms*)base;
464                         tdm_output *output_backend =
465                                 tdm_display_find_output_stamp(private_loop->dpy, output_dpms->output_stamp);
466                         if (!output_backend) {
467                                 TDM_WRN("no output(%f)", output_dpms->output_stamp);
468                                 break;
469                         }
470                         tdm_output_cb_dpms(output_backend, output_dpms->dpms, output_dpms->user_data);
471                         break;
472                         /* LCOV_EXCL_STOP */
473                 }
474                 case TDM_THREAD_CB_PP_DONE: {
475                         tdm_thread_cb_pp_done *pp_done = (tdm_thread_cb_pp_done*)base;
476                         tdm_pp *pp_backend =
477                                 tdm_pp_find_stamp(private_loop->dpy, pp_done->pp_stamp);
478                         if (!pp_backend) {
479                                 TDM_WRN("no pp(%f)", pp_done->pp_stamp);
480                                 break;
481                         }
482                         tdm_pp_cb_done(pp_backend, pp_done->src, pp_done->dst, pp_done->user_data);
483                         break;
484                 }
485                 case TDM_THREAD_CB_CAPTURE_DONE: {
486                         tdm_thread_cb_capture_done *capture_done = (tdm_thread_cb_capture_done*)base;
487                         tdm_capture *capture_backend =
488                                 tdm_capture_find_stamp(private_loop->dpy, capture_done->capture_stamp);
489                         if (!capture_backend) {
490                                 TDM_WRN("no capture(%f)", capture_done->capture_stamp);
491                                 break;
492                         }
493                         tdm_capture_cb_done(capture_backend, capture_done->buffer, capture_done->user_data);
494                         break;
495                 }
496                 case TDM_THREAD_CB_VBLANK_SW: {
497                         tdm_thread_cb_vblank_sw *vblank_sw = (tdm_thread_cb_vblank_sw*)base;
498                         tdm_vblank_cb_vblank_SW(NULL, vblank_sw->vblank_stamp);
499                         break;
500                 }
501                 case TDM_THREAD_CB_VBLANK_CREATE: {
502                         tdm_thread_cb_vblank_create *vblank_create = (tdm_thread_cb_vblank_create*)base;
503                         tdm_vblank_cb_vblank_create(NULL, vblank_create->vblank_stamp);
504                         break;
505                 }
506                 case TDM_THREAD_CB_NEED_VALIDATE: {
507                         tdm_thread_cb_need_validate *ev = (tdm_thread_cb_need_validate*)base;
508                         tdm_output_cb_need_validate(ev->o);
509                         break;
510                 }
511                 default:
512                         break;
513                 }
514                 i += base->length;
515         }
516
517         return TDM_ERROR_NONE;
518 }
519
520 INTERN int
521 tdm_thread_in_display_thread(pid_t tid)
522 {
523         if (!keep_private_thread)
524                 return 1;
525
526         /* DON'T check TDM_MUTEX_IS_LOCKED here */
527
528         return (keep_private_thread->display_tid == tid) ? 1 : 0;
529 }
530
531 INTERN int
532 tdm_thread_is_running(void)
533 {
534         /* DON'T check TDM_MUTEX_IS_LOCKED here */
535
536         return (keep_private_thread) ? 1 : 0;
537 }
538
539 static void
540 _tdm_thread_free_cb(tdm_private_thread_cb *cb)
541 {
542         if (tdm_debug_module & TDM_DEBUG_THREAD)
543                 TDM_INFO("cb(%p) removed", cb);
544
545         assert(LIST_IS_EMPTY(&cb->call_link));
546
547         LIST_DEL(&cb->link);
548         free(cb);
549 }
550
551 static tdm_private_thread_cb *
552 _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)
553 {
554         tdm_private_thread_cb *cb = NULL;
555
556         LIST_FOR_EACH_ENTRY(cb, &cb_list, link) {
557                 if (cb->object == object &&
558                         cb->cb_type == cb_type &&
559                         cb->cb_data == cb_data &&
560                         cb->func == func &&
561                         cb->user_data == user_data &&
562                         cb->owner_tid == owner_tid)
563                         return cb;
564         }
565
566         return NULL;
567 }
568
569 static void
570 _tdm_thread_reset_cb(tdm_thread_cb_type cb_type)
571 {
572         tdm_private_thread_cb *cb = NULL;
573
574         LIST_FOR_EACH_ENTRY(cb, &cb_list, link) {
575                 if (cb->cb_type == cb_type)
576                         cb->called = 0;
577         }
578 }
579
580 INTERN void
581 tdm_thread_cb_set_find_func(tdm_thread_cb_type cb_type, tdm_thread_find_object func)
582 {
583         TDM_RETURN_IF_FAIL(cb_type > 0);
584
585         if (func && find_funcs[cb_type])
586                 TDM_NEVER_GET_HERE();
587
588         find_funcs[cb_type] = func;
589 }
590
591 INTERN tdm_error
592 tdm_thread_cb_add(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data)
593 {
594         tdm_private_thread_cb *cb = NULL;
595         pid_t caller_tid;
596
597         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
598         TDM_RETURN_VAL_IF_FAIL(object != NULL, TDM_ERROR_INVALID_PARAMETER);
599         TDM_RETURN_VAL_IF_FAIL(cb_type > 0, TDM_ERROR_INVALID_PARAMETER);
600         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
601
602         caller_tid = syscall(SYS_gettid);
603
604         cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid);
605         if (cb) {
606                 TDM_ERR("can't be added twice with same data");
607 #if 1
608                 assert(0);
609 #endif
610                 return TDM_ERROR_BAD_REQUEST;
611         }
612
613         cb = calloc(1, sizeof *cb);
614         if (!cb) {
615                 TDM_ERR("calloc failed");
616                 return TDM_ERROR_OUT_OF_MEMORY;
617         }
618
619         LIST_ADDTAIL(&cb->link, &cb_list);
620         LIST_INITHEAD(&cb->call_link);
621
622         cb->object = object;
623         cb->cb_type = cb_type;
624         cb->cb_data = cb_data;
625         cb->func = func;
626         cb->user_data = user_data;
627         cb->owner_tid = caller_tid;
628
629         if (tdm_debug_module & TDM_DEBUG_THREAD)
630                 TDM_INFO("cb_type(%d) cb(%p) added", cb_type, cb);
631
632         return TDM_ERROR_NONE;
633 }
634
635 INTERN void
636 tdm_thread_cb_remove(void *object, tdm_thread_cb_type cb_type, void *cb_data, tdm_thread_cb func, void *user_data)
637 {
638         tdm_private_thread_cb *cb;
639         pid_t caller_tid;
640
641         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
642         TDM_RETURN_IF_FAIL(object != NULL);
643         TDM_RETURN_IF_FAIL(cb_type > 0);
644         TDM_RETURN_IF_FAIL(func != NULL);
645
646         caller_tid = syscall(SYS_gettid);
647
648         cb = _tdm_thread_find_cb(object, cb_type, cb_data, func, user_data, caller_tid);
649         if (!cb)
650                 return;
651
652         _tdm_thread_free_cb(cb);
653 }
654
655 /* when call a callback, we check both cb_base's type and cb_base's data,
656  * because a callback is added with cb_type and cb_data.
657  */
658 INTERN tdm_error
659 tdm_thread_cb_call(void *object, tdm_thread_cb_base *cb_base)
660 {
661         tdm_private_display *private_display = tdm_display_get();
662         tdm_private_thread_cb *cb = NULL, *hh = NULL;
663         int handler_in_other_thread = 0;
664         pid_t caller_tid;
665         struct list_head call_list;
666         tdm_error ret;
667
668         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
669         TDM_RETURN_VAL_IF_FAIL(cb_base != NULL, TDM_ERROR_INVALID_PARAMETER);
670         TDM_RETURN_VAL_IF_FAIL(cb_base->type > 0, TDM_ERROR_INVALID_PARAMETER);
671         TDM_RETURN_VAL_IF_FAIL(cb_base->length > 0, TDM_ERROR_INVALID_PARAMETER);
672         TDM_RETURN_VAL_IF_FAIL(cb_base->sync == 0 || cb_base->sync == 1, TDM_ERROR_INVALID_PARAMETER);
673         TDM_RETURN_VAL_IF_FAIL(cb_base->object_stamp > 0, TDM_ERROR_INVALID_PARAMETER);
674
675         caller_tid = syscall(SYS_gettid);
676
677         assert(find_funcs[cb_base->type] != NULL);
678
679         if (!object) {
680                 object = find_funcs[cb_base->type](private_display, cb_base->object_stamp);
681                 if (!object) {
682                         TDM_WRN("%p gone", object);
683                         return TDM_ERROR_NONE;
684                 }
685         }
686
687         LIST_INITHEAD(&call_list);
688
689         LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &cb_list, link) {
690                 if (cb->called ||
691                         cb->object != object ||
692                         cb->cb_type != cb_base->type ||
693                         cb->cb_data != cb_base->data)
694                         continue;
695
696                 if (cb->owner_tid == caller_tid)
697                         LIST_ADDTAIL(&cb->call_link, &call_list);
698                 else
699                         handler_in_other_thread = 1;
700         }
701
702         if (!LIST_IS_EMPTY(&call_list)) {
703                 LIST_FOR_EACH_ENTRY_SAFE(cb, hh, &call_list, call_link) {
704                         LIST_DELINIT(&cb->call_link);
705                         cb->called = 1;
706                         if (tdm_debug_module & TDM_DEBUG_THREAD)
707                                 TDM_INFO("cb_type(%d) cb(%p) called", cb->cb_type, cb);
708                         cb->func(private_display, cb->object, cb_base, cb->user_data);
709                 }
710         }
711
712         assert(LIST_IS_EMPTY(&call_list));
713
714         if (!handler_in_other_thread) {
715                 _tdm_thread_reset_cb(cb_base->type);
716                 if (keep_private_thread) {
717                         if (cb_base->sync) {
718                                 pthread_cond_signal(&keep_private_thread->event_cond);
719                                 if (tdm_debug_module & TDM_DEBUG_THREAD)
720                                         TDM_INFO("pthread broadcase");
721                         }
722                 }
723                 if (tdm_debug_module & TDM_DEBUG_THREAD)
724                         TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync);
725                 return TDM_ERROR_NONE;
726         }
727
728         /* Once we reach here, it means that keep_private_thread is not NULL.
729          * Just make the crash. Avoiding it is not going to help us.
730          */
731         ret = tdm_thread_send_cb(private_display->private_loop, cb_base);
732         TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED);
733
734         /* waiting until all cb are done in another thread */
735         if (cb_base->sync) {
736                 if (tdm_debug_module & TDM_DEBUG_THREAD)
737                         TDM_INFO("pthread wait");
738
739                 /* pthread_cond_wait atomically release mutex, Upon successful return,
740                  * the mutex shall have been locked and shall be owned by the calling thread
741                  */
742                 tdm_mutex_locked = 0;
743                 pthread_cond_wait(&keep_private_thread->event_cond, &private_display->lock);
744                 tdm_mutex_locked = 1;
745         }
746
747         if (tdm_debug_module & TDM_DEBUG_THREAD)
748                 TDM_INFO("'%s' thread_cb done(sync:%d)", tdm_cb_type_str(cb_base->type), cb_base->sync);
749
750         return TDM_ERROR_NONE;
751 }