2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include <sys/types.h>
25 #include <sys/syscall.h>
33 #include "aul_worker.h"
35 #define DEFAULT_INTERVAL 5000
56 GMainContext *context;
59 struct anr_timer_s *anr_timer;
62 static void __destroy_anr_timer(struct anr_timer_s *timer)
64 g_queue_free(timer->cmd_queue);
68 static struct anr_timer_s *__create_anr_timer(void)
70 struct anr_timer_s *timer;
72 timer = calloc(1, sizeof(struct anr_timer_s));
78 timer->cmd_queue = g_queue_new();
79 if (!timer->cmd_queue) {
80 _E("Failed to create GQueue");
81 __destroy_anr_timer(timer);
88 static void __destroy_job(gpointer data)
90 struct job_s *job = (struct job_s *)data;
92 GMainContext *context;
95 g_io_channel_unref(job->channel);
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);
111 static struct job_s *__create_job(const char *name,
118 job = calloc(1, sizeof(struct job_s));
120 _E("Failed to create job");
124 job->name = strdup(name);
126 _E("Failed to duplicate job name");
131 job->channel = channel;
132 job->callback = callback;
133 job->user_data = user_data;
138 static int __set_comm(const char *name)
141 ssize_t bytes_written;
143 pid_t tid = syscall(__NR_gettid);
145 _I("[%s] TID(%d)", name, tid);
146 snprintf(path, sizeof(path), "/proc/%d/comm", tid);
147 fd = open(path, O_WRONLY);
149 _E("Failed to open %s. error(%d)", path, errno);
153 bytes_written = write(fd, name, strlen(name) + 1);
154 if (bytes_written < 0) {
155 _E("Failed to write name(%s)", name);
165 static GIOCondition __convert_aul_io_condition(int condition)
167 GIOCondition cond = 0;
169 if (condition & AUL_IO_IN)
171 if (condition & AUL_IO_OUT)
173 if (condition & AUL_IO_PRI)
175 if (condition & AUL_IO_HUP)
177 if (condition & AUL_IO_ERR)
179 if (condition & AUL_IO_NVAL)
185 static int __convert_g_io_condition(GIOCondition condition)
189 if (condition & G_IO_IN)
191 if (condition & G_IO_OUT)
193 if (condition & G_IO_PRI)
195 if (condition & G_IO_HUP)
197 if (condition & G_IO_ERR)
199 if (condition & G_IO_NVAL)
205 static gboolean __io_job_handler(GIOChannel *io, GIOCondition condition,
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;
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;
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;
225 _I("[__JOB__] name(%s)", job->name);
228 return G_SOURCE_REMOVE;
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,
236 GIOCondition cond = __convert_aul_io_condition(condition);
237 struct aul_worker_s *worker = (struct aul_worker_s *)handle;
242 if (!worker || !job_name || fd < 0 || !callback) {
243 _E("Invalid parameter");
247 channel = g_io_channel_unix_new(fd);
249 _E("Failed to create GIOChannel");
256 g_io_channel_set_close_on_unref(channel, do_close);
257 source = g_io_create_watch(channel, cond);
259 _E("Failed to create GSource");
260 g_io_channel_unref(channel);
264 job = __create_job(job_name, channel, callback, user_data);
266 _E("Failed to create job(%s)", job_name);
267 g_source_unref(source);
268 g_io_channel_unref(channel);
272 g_source_set_callback(source, (GSourceFunc)__io_job_handler, job, NULL);
273 g_source_set_priority(source, G_PRIORITY_DEFAULT);
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);
281 g_source_unref(source);
286 void aul_worker_remove_io_job(aul_worker_h handle, int fd)
288 struct aul_worker_s *worker = handle;
292 if (worker == NULL || fd < 0) {
293 _E("Invalid parameter");
297 g_mutex_lock(&worker->mutex);
299 while (iter != NULL) {
301 iter = g_list_next(iter);
302 if (job->channel == NULL)
305 if (g_io_channel_unix_get_fd(job->channel) == fd) {
306 worker->jobs = g_list_remove(worker->jobs, job);
311 g_mutex_unlock(&worker->mutex);
314 void aul_worker_destroy(aul_worker_h handle)
316 struct aul_worker_s *worker = (struct aul_worker_s *)handle;
321 if (worker->thread) {
322 if (g_main_loop_is_running(worker->loop))
323 g_main_loop_quit(worker->loop);
325 _E("GMainLoop is not running");
327 g_thread_join(worker->thread);
331 g_main_loop_unref(worker->loop);
334 g_main_context_unref(worker->context);
336 g_cond_clear(&worker->cond);
337 g_mutex_clear(&worker->mutex);
339 __destroy_anr_timer(worker->anr_timer);
344 static gboolean __notify_cb(gpointer data)
346 struct aul_worker_s *worker = (struct aul_worker_s *)data;
348 _W("GMainLoop is started");
349 g_mutex_lock(&worker->mutex);
350 g_cond_signal(&worker->cond);
351 g_mutex_unlock(&worker->mutex);
353 return G_SOURCE_REMOVE;
356 static gpointer __worker_thread_loop(gpointer data)
358 struct aul_worker_s *worker = (struct aul_worker_s *)data;
359 struct anr_timer_s *anr_timer = worker->anr_timer;
362 g_mutex_lock(&worker->mutex);
363 __set_comm(worker->name);
365 source = g_idle_source_new();
367 _E("Failed to create GSource");
368 g_cond_signal(&worker->cond);
369 g_mutex_unlock(&worker->mutex);
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);
378 g_main_context_push_thread_default(worker->context);
379 g_mutex_unlock(&worker->mutex);
380 g_main_loop_run(worker->loop);
382 g_mutex_lock(&worker->mutex);
384 g_source_remove(anr_timer->tag);
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");
394 aul_worker_h aul_worker_create(const char *name)
396 struct aul_worker_s *worker;
399 _E("Invalid parameter");
403 worker = calloc(1, sizeof(struct aul_worker_s));
409 g_mutex_init(&worker->mutex);
410 g_cond_init(&worker->cond);
412 worker->name = strdup(name);
414 _E("Failed to duplicate name");
415 aul_worker_destroy(worker);
419 worker->anr_timer = __create_anr_timer();
420 if (!worker->anr_timer) {
421 _E("Failed to create ANR timer");
422 aul_worker_destroy(worker);
426 worker->context = g_main_context_new();
427 if (!worker->context) {
428 _E("Failed to create GMainContext");
429 aul_worker_destroy(worker);
432 _W("GMainContext(%p)", worker->context);
434 worker->loop = g_main_loop_new(worker->context, FALSE);
436 _E("Failed to create GMainLoop");
437 aul_worker_destroy(worker);
441 g_mutex_lock(&worker->mutex);
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);
451 while (!g_main_loop_is_running(worker->loop))
452 g_cond_wait(&worker->cond, &worker->mutex);
454 g_mutex_unlock(&worker->mutex);
459 static gboolean __timeout_cb(gpointer data)
461 struct aul_worker_s *worker = data;
462 struct anr_timer_s *anr_timer;
467 _E("Application Not Responding");
468 g_mutex_lock(&worker->mutex);
469 anr_timer = worker->anr_timer;
471 anr_timer->start_time = 0;
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,
483 g_mutex_unlock(&worker->mutex);
485 return G_SOURCE_REMOVE;
488 int aul_worker_add_anr_timer(aul_worker_h handle, int cmd, unsigned int timeout)
490 struct aul_worker_s *worker = handle;
491 struct anr_timer_s *anr_timer;
492 unsigned int elapsed_time;
493 unsigned int interval;
498 _E("Invalid parameter");
502 g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&worker->mutex);
504 anr_timer = worker->anr_timer;
505 if (anr_timer->tag) {
506 source = g_main_context_find_source_by_id(worker->context,
508 g_source_destroy(source);
512 g_queue_push_tail(anr_timer->cmd_queue, GINT_TO_POINTER(cmd));
514 current_time = g_get_monotonic_time();
515 if (anr_timer->start_time == 0) {
517 anr_timer->start_time = current_time;
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;
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);
533 int aul_worker_remove_anr_timer(aul_worker_h handle)
535 struct aul_worker_s *worker = handle;
536 struct anr_timer_s *anr_timer;
540 _E("Invalid parameter");
544 g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&worker->mutex);
546 anr_timer = worker->anr_timer;
547 if (g_queue_is_empty(anr_timer->cmd_queue)) {
548 _E("Queue is empty");
552 g_queue_pop_head(anr_timer->cmd_queue);
553 if (!g_queue_is_empty(anr_timer->cmd_queue))
556 if (anr_timer->tag) {
557 source = g_main_context_find_source_by_id(worker->context,
559 g_source_destroy(source);
561 anr_timer->start_time = 0;