Move function definition to aul header
[platform/core/appfw/aul-1.git] / src / aul_worker.c
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <pthread.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/syscall.h>
26 #include <fcntl.h>
27 #include <glib.h>
28 #include <gio/gio.h>
29
30 #include "aul.h"
31 #include "aul_util.h"
32 #include "aul_sock.h"
33 #include "aul_worker.h"
34
35 #define DEFAULT_INTERVAL 5000
36
37 struct job_s {
38         char *name;
39         GIOChannel *channel;
40         guint tag;
41         void *callback;
42         void *user_data;
43 };
44
45 struct anr_timer_s {
46         guint tag;
47         gint64 start_time;
48         GQueue *cmd_queue;
49 };
50
51 struct aul_worker_s {
52         char *name;
53         GThread *thread;
54         GMutex mutex;
55         GCond cond;
56         GMainContext *context;
57         GMainLoop *loop;
58         GList *jobs;
59         struct anr_timer_s *anr_timer;
60 };
61
62 static void __destroy_anr_timer(struct anr_timer_s *timer)
63 {
64         g_queue_free(timer->cmd_queue);
65         free(timer);
66 }
67
68 static struct anr_timer_s *__create_anr_timer(void)
69 {
70         struct anr_timer_s *timer;
71
72         timer = calloc(1, sizeof(struct anr_timer_s));
73         if (!timer) {
74                 _E("Out of memory");
75                 return NULL;
76         }
77
78         timer->cmd_queue = g_queue_new();
79         if (!timer->cmd_queue) {
80                 _E("Failed to create GQueue");
81                 __destroy_anr_timer(timer);
82                 return NULL;
83         }
84
85         return timer;
86 }
87
88 static void __destroy_job(gpointer data)
89 {
90         struct job_s *job = (struct job_s *)data;
91         GSource *source;
92         GMainContext *context;
93
94         if (job->channel)
95                 g_io_channel_unref(job->channel);
96
97         if (job->tag) {
98                 context = g_main_context_get_thread_default();
99                 source = g_main_context_find_source_by_id(context, job->tag);
100                 _W("GMainContext(%p), GSource(%p)", context, source);
101                 if (source && !g_source_is_destroyed(source)) {
102                         _W("Destroy GSource(%p)", source);
103                         g_source_destroy(source);
104                 }
105         }
106
107         free(job->name);
108         free(job);
109 }
110
111 static struct job_s *__create_job(const char *name,
112                 GIOChannel *channel,
113                 void *callback,
114                 void *user_data)
115 {
116         struct job_s *job;
117
118         job = calloc(1, sizeof(struct job_s));
119         if (!job) {
120                 _E("Failed to create job");
121                 return NULL;
122         }
123
124         job->name = strdup(name);
125         if (!job->name) {
126                 _E("Failed to duplicate job name");
127                 __destroy_job(job);
128                 return NULL;
129         }
130
131         job->channel = channel;
132         job->callback = callback;
133         job->user_data = user_data;
134
135         return job;
136 }
137
138 static int __set_comm(const char *name)
139 {
140         int fd;
141         ssize_t bytes_written;
142         char path[PATH_MAX];
143         pid_t tid = syscall(__NR_gettid);
144
145         _I("[%s] TID(%d)", name, tid);
146         snprintf(path, sizeof(path), "/proc/%d/comm", tid);
147         fd = open(path, O_WRONLY);
148         if (fd < 0) {
149                 _E("Failed to open %s. error(%d)", path, errno);
150                 return -1;
151         }
152
153         bytes_written = write(fd, name, strlen(name) + 1);
154         if (bytes_written < 0) {
155                 _E("Failed to write name(%s)", name);
156                 close(fd);
157                 return -1;
158         }
159
160         close(fd);
161
162         return 0;
163 }
164
165 static GIOCondition __convert_aul_io_condition(int condition)
166 {
167         GIOCondition cond = 0;
168
169         if (condition & AUL_IO_IN)
170                 cond |= G_IO_IN;
171         if (condition & AUL_IO_OUT)
172                 cond |= G_IO_OUT;
173         if (condition & AUL_IO_PRI)
174                 cond |= G_IO_PRI;
175         if (condition & AUL_IO_HUP)
176                 cond |= G_IO_HUP;
177         if (condition & AUL_IO_ERR)
178                 cond |= G_IO_ERR;
179         if (condition & AUL_IO_NVAL)
180                 cond |= G_IO_NVAL;
181
182         return cond;
183 }
184
185 static int __convert_g_io_condition(GIOCondition condition)
186 {
187         int cond = 0;
188
189         if (condition & G_IO_IN)
190                 cond |= AUL_IO_IN;
191         if (condition & G_IO_OUT)
192                 cond |= AUL_IO_OUT;
193         if (condition & G_IO_PRI)
194                 cond |= AUL_IO_PRI;
195         if (condition & G_IO_HUP)
196                 cond |= AUL_IO_HUP;
197         if (condition & G_IO_ERR)
198                 cond |= AUL_IO_ERR;
199         if (condition & G_IO_NVAL)
200                 cond |= AUL_IO_NVAL;
201
202         return cond;
203 }
204
205 static gboolean __io_job_handler(GIOChannel *io, GIOCondition condition,
206                 gpointer data)
207 {
208         int fd = g_io_channel_unix_get_fd(io);
209         struct job_s *job = (struct job_s *)data;
210         aul_worker_io_job_cb callback;
211         GSource *source;
212         int cond;
213
214         source = g_main_current_source();
215         if (!source || g_source_is_destroyed(source)) {
216                 _E("[__JOB__] GSource(%p) is destroyed", source);
217                 return G_SOURCE_REMOVE;
218         }
219
220         cond = __convert_g_io_condition(condition);
221         callback = (aul_worker_io_job_cb)job->callback;
222         if (callback(fd, cond, job->user_data))
223                 return G_SOURCE_CONTINUE;
224
225         _I("[__JOB__] name(%s)", job->name);
226         job->tag = 0;
227
228         return G_SOURCE_REMOVE;
229 }
230
231 int aul_worker_add_io_job(aul_worker_h handle, const char *job_name,
232                 int fd, int condition, bool do_close,
233                 aul_worker_io_job_cb callback,
234                 void *user_data)
235 {
236         GIOCondition cond = __convert_aul_io_condition(condition);
237         struct aul_worker_s *worker = (struct aul_worker_s *)handle;
238         struct job_s *job;
239         GIOChannel *channel;
240         GSource *source;
241
242         if (!worker || !job_name || fd < 0 || !callback) {
243                 _E("Invalid parameter");
244                 return AUL_R_EINVAL;
245         }
246
247         channel = g_io_channel_unix_new(fd);
248         if (!channel) {
249                 _E("Failed to create GIOChannel");
250                 if (do_close)
251                         close(fd);
252
253                 return AUL_R_ENOMEM;
254         }
255
256         g_io_channel_set_close_on_unref(channel, do_close);
257         source = g_io_create_watch(channel, cond);
258         if (!source) {
259                 _E("Failed to create GSource");
260                 g_io_channel_unref(channel);
261                 return AUL_R_ENOMEM;
262         }
263
264         job = __create_job(job_name, channel, callback, user_data);
265         if (!job) {
266                 _E("Failed to create job(%s)", job_name);
267                 g_source_unref(source);
268                 g_io_channel_unref(channel);
269                 return AUL_R_ENOMEM;
270         }
271
272         g_source_set_callback(source, (GSourceFunc)__io_job_handler, job, NULL);
273         g_source_set_priority(source, G_PRIORITY_DEFAULT);
274
275         g_mutex_lock(&worker->mutex);
276         worker->jobs = g_list_append(worker->jobs, job);
277         job->tag = g_source_attach(source, worker->context);
278         _W("GMainContext(%p), GSource(%p)", worker->context, source);
279         g_mutex_unlock(&worker->mutex);
280
281         g_source_unref(source);
282
283         return AUL_R_OK;
284 }
285
286 void aul_worker_remove_io_job(aul_worker_h handle, int fd)
287 {
288         struct aul_worker_s *worker = handle;
289         struct job_s *job;
290         GList *iter;
291
292         if (worker == NULL || fd < 0) {
293                 _E("Invalid parameter");
294                 return;
295         }
296
297         g_mutex_lock(&worker->mutex);
298         iter = worker->jobs;
299         while (iter != NULL) {
300                 job = iter->data;
301                 iter = g_list_next(iter);
302                 if (job->channel == NULL)
303                         continue;
304
305                 if (g_io_channel_unix_get_fd(job->channel) == fd) {
306                         worker->jobs = g_list_remove(worker->jobs, job);
307                         __destroy_job(job);
308                         break;
309                 }
310         }
311         g_mutex_unlock(&worker->mutex);
312 }
313
314 void aul_worker_destroy(aul_worker_h handle)
315 {
316         struct aul_worker_s *worker = (struct aul_worker_s *)handle;
317
318         if (!worker)
319                 return;
320
321         if (worker->thread) {
322                 if (g_main_loop_is_running(worker->loop))
323                         g_main_loop_quit(worker->loop);
324                 else
325                         _E("GMainLoop is not running");
326
327                 g_thread_join(worker->thread);
328         }
329
330         if (worker->loop)
331                 g_main_loop_unref(worker->loop);
332
333         if (worker->context)
334                 g_main_context_unref(worker->context);
335
336         g_cond_clear(&worker->cond);
337         g_mutex_clear(&worker->mutex);
338
339         __destroy_anr_timer(worker->anr_timer);
340         free(worker->name);
341         free(worker);
342 }
343
344 static gboolean __notify_cb(gpointer data)
345 {
346         struct aul_worker_s *worker = (struct aul_worker_s *)data;
347
348         _W("GMainLoop is started");
349         g_mutex_lock(&worker->mutex);
350         g_cond_signal(&worker->cond);
351         g_mutex_unlock(&worker->mutex);
352
353         return G_SOURCE_REMOVE;
354 }
355
356 static gpointer __worker_thread_loop(gpointer data)
357 {
358         struct aul_worker_s *worker = (struct aul_worker_s *)data;
359         struct anr_timer_s *anr_timer = worker->anr_timer;
360         GSource *source;
361
362         g_mutex_lock(&worker->mutex);
363         __set_comm(worker->name);
364
365         source = g_idle_source_new();
366         if (!source) {
367                 _E("Failed to create GSource");
368                 g_cond_signal(&worker->cond);
369                 g_mutex_unlock(&worker->mutex);
370                 return NULL;
371         }
372
373         g_source_set_callback(source, (GSourceFunc)__notify_cb, worker, NULL);
374         g_source_set_priority(source, G_PRIORITY_HIGH);
375         g_source_attach(source, worker->context);
376         g_source_unref(source);
377
378         g_main_context_push_thread_default(worker->context);
379         g_mutex_unlock(&worker->mutex);
380         g_main_loop_run(worker->loop);
381
382         g_mutex_lock(&worker->mutex);
383         if (anr_timer->tag)
384                 g_source_remove(anr_timer->tag);
385
386         g_list_free_full(worker->jobs, __destroy_job);
387         g_main_context_pop_thread_default(worker->context);
388         g_mutex_unlock(&worker->mutex);
389         _W("Shut down worker");
390
391         return NULL;
392 }
393
394 aul_worker_h aul_worker_create(const char *name)
395 {
396         struct aul_worker_s *worker;
397
398         if (!name) {
399                 _E("Invalid parameter");
400                 return NULL;
401         }
402
403         worker = calloc(1, sizeof(struct aul_worker_s));
404         if (!worker) {
405                 _E("Out of memory");
406                 return NULL;
407         }
408
409         g_mutex_init(&worker->mutex);
410         g_cond_init(&worker->cond);
411
412         worker->name = strdup(name);
413         if (!worker->name) {
414                 _E("Failed to duplicate name");
415                 aul_worker_destroy(worker);
416                 return NULL;
417         }
418
419         worker->anr_timer = __create_anr_timer();
420         if (!worker->anr_timer) {
421                 _E("Failed to create ANR timer");
422                 aul_worker_destroy(worker);
423                 return NULL;
424         }
425
426         worker->context = g_main_context_new();
427         if (!worker->context) {
428                 _E("Failed to create GMainContext");
429                 aul_worker_destroy(worker);
430                 return NULL;
431         }
432         _W("GMainContext(%p)", worker->context);
433
434         worker->loop = g_main_loop_new(worker->context, FALSE);
435         if (!worker->loop) {
436                 _E("Failed to create GMainLoop");
437                 aul_worker_destroy(worker);
438                 return NULL;
439         }
440
441         g_mutex_lock(&worker->mutex);
442
443         worker->thread = g_thread_new(name, __worker_thread_loop, worker);
444         if (!worker->thread) {
445                 _E("Failed to create worker thread");
446                 g_mutex_unlock(&worker->mutex);
447                 aul_worker_destroy(worker);
448                 return NULL;
449         }
450
451         while (!g_main_loop_is_running(worker->loop))
452                 g_cond_wait(&worker->cond, &worker->mutex);
453
454         g_mutex_unlock(&worker->mutex);
455
456         return worker;
457 }
458
459 static gboolean __timeout_cb(gpointer data)
460 {
461         struct aul_worker_s *worker = data;
462         struct anr_timer_s *anr_timer;
463         char buf[12];
464         bundle *b;
465         int cmd;
466
467         _E("Application Not Responding");
468         g_mutex_lock(&worker->mutex);
469         anr_timer = worker->anr_timer;
470         anr_timer->tag = 0;
471         anr_timer->start_time = 0;
472
473         b = bundle_create();
474         while (!g_queue_is_empty(anr_timer->cmd_queue)) {
475                 cmd = GPOINTER_TO_INT(g_queue_pop_head(anr_timer->cmd_queue));
476                 snprintf(buf, sizeof(buf), "%d", cmd);
477                 bundle_del(b, AUL_K_COMMAND);
478                 bundle_add_str(b, AUL_K_COMMAND, buf);
479                 aul_sock_send_bundle(AUL_UTIL_PID, getuid(), ANR_NOTIFY, b,
480                                 AUL_SOCK_NOREPLY);
481         }
482         bundle_free(b);
483         g_mutex_unlock(&worker->mutex);
484
485         return G_SOURCE_REMOVE;
486 }
487
488 int aul_worker_add_anr_timer(aul_worker_h handle, int cmd, unsigned int timeout)
489 {
490         struct aul_worker_s *worker = handle;
491         struct anr_timer_s *anr_timer;
492         unsigned int elapsed_time;
493         unsigned int interval;
494         gint64 current_time;
495         GSource *source;
496
497         if (!worker) {
498                 _E("Invalid parameter");
499                 return AUL_R_EINVAL;
500         }
501
502         g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&worker->mutex);
503
504         anr_timer = worker->anr_timer;
505         if (anr_timer->tag) {
506                 source = g_main_context_find_source_by_id(worker->context,
507                                 anr_timer->tag);
508                 g_source_destroy(source);
509                 anr_timer->tag = 0;
510         }
511
512         g_queue_push_tail(anr_timer->cmd_queue, GINT_TO_POINTER(cmd));
513
514         current_time = g_get_monotonic_time();
515         if (anr_timer->start_time == 0) {
516                 interval = timeout;
517                 anr_timer->start_time = current_time;
518         } else {
519                 elapsed_time = (unsigned int)((current_time -
520                                 anr_timer->start_time) / 1000);
521                 interval = timeout + (DEFAULT_INTERVAL - elapsed_time);
522                 anr_timer->start_time = current_time;
523         }
524
525         source = g_timeout_source_new(interval);
526         g_source_set_callback(source, (GSourceFunc)__timeout_cb, worker, NULL);
527         anr_timer->tag = g_source_attach(source, worker->context);
528         g_source_unref(source);
529
530         return AUL_R_OK;
531 }
532
533 int aul_worker_remove_anr_timer(aul_worker_h handle)
534 {
535         struct aul_worker_s *worker = handle;
536         struct anr_timer_s *anr_timer;
537         GSource *source;
538
539         if (!worker) {
540                 _E("Invalid parameter");
541                 return AUL_R_EINVAL;
542         }
543
544         g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&worker->mutex);
545
546         anr_timer = worker->anr_timer;
547         if (g_queue_is_empty(anr_timer->cmd_queue)) {
548                 _E("Queue is empty");
549                 return AUL_R_ERROR;
550         }
551
552         g_queue_pop_head(anr_timer->cmd_queue);
553         if (!g_queue_is_empty(anr_timer->cmd_queue))
554                 return AUL_R_OK;
555
556         if (anr_timer->tag) {
557                 source = g_main_context_find_source_by_id(worker->context,
558                                 anr_timer->tag);
559                 g_source_destroy(source);
560                 anr_timer->tag = 0;
561                 anr_timer->start_time = 0;
562         }
563
564         return AUL_R_OK;
565 }