fix deadlock and enhance lock/unlock to protect the backend module's data
[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 <sys/socket.h>
41
42 #include "tdm.h"
43 #include "tdm_private.h"
44 #include "tdm_list.h"
45
46 static tdm_private_thread *keep_private_thread;
47
48 struct _tdm_private_thread {
49         tdm_private_loop *private_loop;
50
51         pthread_t event_thread;
52
53         pid_t display_tid;
54         pid_t thread_tid;
55
56         /* 0: read, 1: write */
57         int pipe[2];
58 };
59
60 static void*
61 _tdm_thread_main(void *data)
62 {
63         tdm_private_thread *private_thread = (tdm_private_thread*)data;
64         tdm_private_loop *private_loop = private_thread->private_loop;
65         int fd;
66         struct pollfd fds;
67         int ret;
68
69         /* Not lock/unlock for the private_thread and private_loop structure
70          * because they won't be destroyed as long as tdm thread is running.
71          * When they're destroyed, we have already exit the tdm thread.
72          */
73         private_thread->thread_tid = syscall(SYS_gettid);
74
75         TDM_INFO("display_tid:%d, thread_tid: %d",
76                  private_thread->display_tid, private_thread->thread_tid);
77
78         fd = tdm_event_loop_get_fd(private_loop->dpy);
79         if (fd < 0) {
80                 TDM_ERR("couldn't get fd");
81                 goto exit_thread;
82         }
83
84         fds.events = POLLIN;
85         fds.fd = fd;
86         fds.revents = 0;
87
88         while (1) {
89                 if (tdm_debug_thread)
90                         TDM_INFO("server flush");
91                 tdm_event_loop_flush(private_loop->dpy);
92
93                 if (tdm_debug_thread)
94                         TDM_INFO("fd(%d) polling in", fd);
95
96                 ret = poll(&fds, 1, -1);
97
98                 if (tdm_debug_thread)
99                         TDM_INFO("fd(%d) polling out", fd);
100
101                 if (ret < 0) {
102                         if (errno == EBUSY)  /* normal case */
103                                 continue;
104                         else {
105                                 TDM_ERR("poll failed: %m");
106                                 goto exit_thread;
107                         }
108                 }
109
110                 if (tdm_debug_thread)
111                         TDM_INFO("thread got events");
112
113                 if (tdm_event_loop_dispatch(private_loop->dpy) < 0)
114                         TDM_ERR("dispatch error");
115         }
116
117 exit_thread:
118         pthread_exit(NULL);
119 }
120
121 /* NOTE: tdm thread doesn't care about multi-thread. */
122 INTERN tdm_error
123 tdm_thread_init(tdm_private_loop *private_loop)
124 {
125         tdm_private_display *private_display;
126         tdm_private_thread *private_thread;
127         const char *thread;
128
129         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
130         TDM_RETURN_VAL_IF_FAIL(private_loop->dpy, TDM_ERROR_OPERATION_FAILED);
131
132         private_display = private_loop->dpy;
133         TDM_RETURN_VAL_IF_FAIL(private_display->private_loop, TDM_ERROR_OPERATION_FAILED);
134
135         if (private_loop->private_thread)
136                 return TDM_ERROR_NONE;
137
138         /* enable as default */
139         thread = getenv("TDM_THREAD");
140         if (!thread || strncmp(thread, "1", 1)) {
141                 TDM_INFO("not using a TDM event thread: %s", (thread)?thread:"none");
142                 return TDM_ERROR_NONE;
143         }
144
145         private_thread = calloc(1, sizeof *private_thread);
146         if (!private_thread) {
147                 TDM_ERR("alloc failed");
148                 return TDM_ERROR_OUT_OF_MEMORY;
149         }
150
151         if (pipe(private_thread->pipe) != 0) {
152                 TDM_ERR("socketpair failed: %m");
153                 free(private_thread);
154                 return TDM_ERROR_OPERATION_FAILED;
155         }
156
157         private_thread->private_loop = private_loop;
158         private_loop->private_thread = private_thread;
159
160         private_thread->display_tid = syscall(SYS_gettid);
161
162         pthread_create(&private_thread->event_thread, NULL, _tdm_thread_main,
163                        private_thread);
164
165         keep_private_thread = private_thread;
166
167         TDM_INFO("using a TDM event thread. pipe(%d,%d)",
168                  private_thread->pipe[0], private_thread->pipe[1]);
169
170         return TDM_ERROR_NONE;
171 }
172
173 INTERN void
174 tdm_thread_deinit(tdm_private_loop *private_loop)
175 {
176         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
177
178         if (!private_loop->private_thread)
179                 return;
180
181         pthread_cancel(private_loop->private_thread->event_thread);
182         pthread_join(private_loop->private_thread->event_thread, NULL);
183
184         if (private_loop->private_thread->pipe[0] >= 0)
185                 close(private_loop->private_thread->pipe[0]);
186         if (private_loop->private_thread->pipe[1] >= 0)
187                 close(private_loop->private_thread->pipe[1]);
188
189         free(private_loop->private_thread);
190         private_loop->private_thread = NULL;
191         keep_private_thread = NULL;
192
193         TDM_INFO("Finish a TDM event thread");
194 }
195
196 INTERN int
197 tdm_thread_get_fd(tdm_private_loop *private_loop)
198 {
199         tdm_private_thread *private_thread;
200
201         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
202         TDM_RETURN_VAL_IF_FAIL(private_loop, -1);
203         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, -1);
204
205         private_thread = private_loop->private_thread;
206
207         return private_thread->pipe[0];
208 }
209
210 INTERN tdm_error
211 tdm_thread_send_cb(tdm_private_loop *private_loop, tdm_thread_cb_base *base)
212 {
213         tdm_private_thread *private_thread;
214         ssize_t len;
215
216         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
217         TDM_RETURN_VAL_IF_FAIL(base, TDM_ERROR_INVALID_PARAMETER);
218         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_INVALID_PARAMETER);
219         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, TDM_ERROR_INVALID_PARAMETER);
220
221         private_thread = private_loop->private_thread;
222
223         if (tdm_debug_thread)
224                 TDM_INFO("fd(%d) type(%d), length(%d)",
225                          private_thread->pipe[1], base->type, base->length);
226
227         len = write(private_thread->pipe[1], base, base->length);
228         if (len != base->length) {
229                 TDM_ERR("write failed (%d != %d): %m", (int)len, base->length);
230                 return TDM_ERROR_OPERATION_FAILED;
231         }
232
233         return TDM_ERROR_NONE;
234 }
235
236 INTERN tdm_error
237 tdm_thread_handle_cb(tdm_private_loop *private_loop)
238 {
239         tdm_private_display *private_display;
240         tdm_private_thread *private_thread;
241         tdm_thread_cb_base *base;
242         char buffer[1024];
243         int len, i;
244
245         /* DON'T check TDM_MUTEX_IS_LOCKED here */
246
247         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_INVALID_PARAMETER);
248         TDM_RETURN_VAL_IF_FAIL(private_loop->private_thread, TDM_ERROR_INVALID_PARAMETER);
249
250         private_thread = private_loop->private_thread;
251         private_display = private_loop->dpy;
252
253         len = read(private_thread->pipe[0], buffer, sizeof buffer);
254
255         if (tdm_debug_thread)
256                 TDM_INFO("fd(%d) read length(%d)", private_thread->pipe[0], len);
257
258         if (len == 0)
259                 return TDM_ERROR_NONE;
260
261         if (len < sizeof *base) {
262                 TDM_NEVER_GET_HERE();
263                 return TDM_ERROR_OPERATION_FAILED;
264         }
265
266         _pthread_mutex_lock(&private_display->lock);
267
268         i = 0;
269         while (i < len) {
270                 base = (tdm_thread_cb_base*)&buffer[i];
271                 if (tdm_debug_thread)
272                         TDM_INFO("type(%d), length(%d)", base->type, base->length);
273                 switch (base->type) {
274                 case TDM_THREAD_CB_OUTPUT_COMMIT:
275                 {
276                         tdm_thread_cb_output_commit *output_commit = (tdm_thread_cb_output_commit*)base;
277                         tdm_output *output_backend =
278                                 tdm_display_find_output_stamp(private_loop->dpy, output_commit->output_stamp);
279                         if (!output_backend) {
280                                 TDM_WRN("no output(%ld)", output_commit->output_stamp);
281                                 break;
282                         }
283                         tdm_output_cb_commit(output_backend, output_commit->sequence,
284                                              output_commit->tv_sec, output_commit->tv_usec,
285                                              output_commit->user_data);
286                         break;
287                 }
288                 case TDM_THREAD_CB_OUTPUT_VBLANK:
289                 {
290                         tdm_thread_cb_output_vblank *output_vblank = (tdm_thread_cb_output_vblank*)base;
291                         tdm_output *output_backend =
292                                 tdm_display_find_output_stamp(private_loop->dpy, output_vblank->output_stamp);
293                         if (!output_backend) {
294                                 TDM_WRN("no output(%ld)", output_vblank->output_stamp);
295                                 break;
296                         }
297                         tdm_output_cb_vblank(output_backend, output_vblank->sequence,
298                                              output_vblank->tv_sec, output_vblank->tv_usec,
299                                              output_vblank->user_data);
300                         break;
301                 }
302                 case TDM_THREAD_CB_OUTPUT_STATUS:
303                 {
304                         tdm_thread_cb_output_status *output_status = (tdm_thread_cb_output_status*)base;
305                         tdm_output *output_backend =
306                                 tdm_display_find_output_stamp(private_loop->dpy, output_status->output_stamp);
307                         if (!output_backend) {
308                                 TDM_WRN("no output(%ld)", output_status->output_stamp);
309                                 break;
310                         }
311                         tdm_output_cb_status(output_backend, output_status->status,
312                                              output_status->user_data);
313                         break;
314                 }
315                 case TDM_THREAD_CB_PP_DONE:
316                 {
317                         tdm_thread_cb_pp_done *pp_done = (tdm_thread_cb_pp_done*)base;
318                         tdm_pp *pp_backend =
319                                 tdm_pp_find_stamp(private_loop->dpy, pp_done->pp_stamp);
320                         if (!pp_backend) {
321                                 TDM_WRN("no pp(%ld)", pp_done->pp_stamp);
322                                 break;
323                         }
324                         tdm_pp_cb_done(pp_backend, pp_done->src, pp_done->dst, pp_done->user_data);
325                         break;
326                 }
327                 case TDM_THREAD_CB_CAPTURE_DONE:
328                 {
329                         tdm_thread_cb_capture_done *capture_done = (tdm_thread_cb_capture_done*)base;
330                         tdm_capture *capture_backend =
331                                 tdm_capture_find_stamp(private_loop->dpy, capture_done->capture_stamp);
332                         if (!capture_backend) {
333                                 TDM_WRN("no capture(%ld)", capture_done->capture_stamp);
334                                 break;
335                         }
336                         tdm_capture_cb_done(capture_backend, capture_done->buffer, capture_done->user_data);
337                         break;
338                 }
339                 default:
340                         break;
341                 }
342                 i += base->length;
343         }
344
345         _pthread_mutex_unlock(&private_display->lock);
346
347         tdm_event_loop_flush(private_loop->dpy);
348
349         return TDM_ERROR_NONE;
350 }
351
352 INTERN int
353 tdm_thread_in_display_thread(pid_t tid)
354 {
355         if (!keep_private_thread)
356                 return 1;
357
358         /* DON'T check TDM_MUTEX_IS_LOCKED here */
359
360         return (keep_private_thread->display_tid == tid) ? 1 : 0;
361 }
362
363 INTERN int
364 tdm_thread_is_running(void)
365 {
366         /* DON'T check TDM_MUTEX_IS_LOCKED here */
367
368         return (keep_private_thread) ? 1 : 0;
369 }