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