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