Make thread safe code 93/207793/4
authorHwankyu Jhun <h.jhun@samsung.com>
Thu, 13 Jun 2019 00:33:01 +0000 (09:33 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Thu, 13 Jun 2019 22:33:07 +0000 (07:33 +0900)
To avoid concurrent access to the request context handle, this patch
synchronizes the launch request.

Change-Id: I54daccb570b4afc7dc3b7678185258c99148f7d9
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/app_control.c

index b899a4f..033c570 100644 (file)
@@ -20,6 +20,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <pthread.h>
 
 #include <glib.h>
 #include <bundle.h>
@@ -80,6 +81,7 @@ typedef struct app_control_request_context_s {
 } *app_control_request_context_h;
 
 struct launch_request_s {
+       int id;
        bool implicit_default_operation;
        app_control_request_context_h request_context;
        app_control_h app_control;
@@ -97,9 +99,298 @@ struct app_control_action_s {
        void *user_data;
 };
 
+struct reply_info_s {
+       bundle *b;
+       aul_svc_result_val result;
+       void *user_data;
+};
+
+struct result_info_s {
+       int result;
+       void *user_data;
+};
+
+struct pending_item_s {
+       int id;
+       struct reply_info_s *reply_info;
+       struct result_info_s *result_info;
+};
+
 typedef int (*launch_request_handler)(struct launch_request_s *req);
 
 static int app_control_create_reply(bundle *data, struct app_control_s **app_control);
+static void app_control_request_reply_broker(bundle *appsvc_bundle,
+               int appsvc_request_code, aul_svc_result_val appsvc_result,
+               void *appsvc_data);
+static void app_control_request_result_broker(int request_code, int result,
+               void *user_data);
+
+static pthread_mutex_t __mutex = PTHREAD_MUTEX_INITIALIZER;
+static GList *__pending_list;
+
+static int __generate_request_id(void)
+{
+       static int id;
+
+       g_atomic_int_inc(&id);
+       return id;
+}
+
+static void __destroy_result_info(gpointer data)
+{
+       struct result_info_s *info = data;
+
+       if (!info)
+               return;
+
+       free(info);
+}
+
+static struct result_info_s *__create_result_info(int result,
+               void *user_data)
+{
+       struct result_info_s *info;
+
+       info = calloc(1, sizeof(struct result_info_s));
+       if (!info) {
+               LOGE("Out of memory");
+               return NULL;
+       }
+
+       info->result = result;
+       info->user_data = user_data;
+
+       return info;
+}
+
+static void __destroy_reply_info(gpointer data)
+{
+       struct reply_info_s *info = data;
+
+       if (!info)
+               return;
+
+       if (info->b)
+               bundle_free(info->b);
+
+       free(info);
+}
+
+static struct reply_info_s *__create_reply_info(bundle *b,
+               aul_svc_result_val result, void *user_data)
+{
+       struct reply_info_s *info;
+
+       info = calloc(1, sizeof(struct reply_info_s));
+       if (!info) {
+               LOGE("Out of memory");
+               return NULL;
+       }
+
+       info->b = bundle_dup(b);
+       if (!info->b) {
+               LOGE("Failed to duplicate bundle");
+               free(info);
+               return NULL;
+       }
+
+       info->result = result;
+       info->user_data = user_data;
+
+       return info;
+}
+
+static void __destroy_pending_item(gpointer data)
+{
+       struct pending_item_s *item = data;
+
+       if (!item)
+               return;
+
+       if (item->reply_info)
+               __destroy_reply_info(item->reply_info);
+
+       if (item->result_info)
+               __destroy_result_info(item->result_info);
+
+       free(item);
+}
+
+static struct pending_item_s *__create_pending_item(int id)
+{
+       struct pending_item_s *item;
+
+       item = calloc(1, sizeof(struct pending_item_s));
+       if (!item) {
+               LOGE("Out of memory");
+               return NULL;
+       }
+
+       item->id = id;
+
+       return item;
+}
+
+static void __add_pending_item(int id)
+{
+       struct pending_item_s *item;
+
+       item = __create_pending_item(id);
+       if (!item)
+               return;
+
+       pthread_mutex_lock(&__mutex);
+       __pending_list = g_list_prepend(__pending_list, item);
+       pthread_mutex_unlock(&__mutex);
+}
+
+static void __remove_pending_item(int id)
+{
+       struct pending_item_s *item;
+       GList *iter;
+
+       pthread_mutex_lock(&__mutex);
+       iter = __pending_list;
+       while (iter) {
+               item = (struct pending_item_s *)iter->data;
+               iter = g_list_next(iter);
+               if (item->id == id) {
+                       __pending_list = g_list_remove(__pending_list, item);
+                       __destroy_pending_item(item);
+                       break;
+               }
+       }
+       pthread_mutex_unlock(&__mutex);
+}
+
+static struct pending_item_s *__pop_pending_item(int id)
+{
+       struct pending_item_s *item;
+       GList *iter;
+
+       pthread_mutex_lock(&__mutex);
+       iter = __pending_list;
+       while (iter) {
+               item = (struct pending_item_s *)iter->data;
+               iter = g_list_next(iter);
+               if (item->id == id) {
+                       __pending_list = g_list_remove(__pending_list, item);
+                       pthread_mutex_unlock(&__mutex);
+                       return item;
+               }
+       }
+       pthread_mutex_unlock(&__mutex);
+
+       return NULL;
+}
+
+static gboolean __process_pending_item(gpointer data)
+{
+       struct pending_item_s *item = data;
+       struct result_info_s *result_info;
+       struct reply_info_s *reply_info;
+
+       if (!item)
+               return G_SOURCE_REMOVE;
+
+       if (item->result_info) {
+               result_info = item->result_info;
+               app_control_request_result_broker(item->id,
+                               result_info->result, result_info->user_data);
+               __destroy_result_info(result_info);
+               item->result_info = NULL;
+               return G_SOURCE_CONTINUE;
+       }
+
+       if (item->reply_info) {
+               reply_info = item->reply_info;
+               app_control_request_reply_broker(reply_info->b, item->id,
+                               reply_info->result, reply_info->user_data);
+               __destroy_reply_info(reply_info);
+               item->reply_info = NULL;
+       }
+       __destroy_pending_item(item);
+
+       return G_SOURCE_REMOVE;
+}
+
+static void __flush_pending_item(int id)
+{
+       struct pending_item_s *item;
+
+       item = __pop_pending_item(id);
+       if (!item)
+               return;
+
+       if (!item->reply_info && !item->result_info) {
+               __destroy_pending_item(item);
+               return;
+       }
+
+       g_idle_add(__process_pending_item, item);
+}
+
+static bool __exist_pending_item(int id)
+{
+       struct pending_item_s *item;
+       GList *iter;
+
+       pthread_mutex_lock(&__mutex);
+       iter = __pending_list;
+       while (iter) {
+               item = (struct pending_item_s *)iter->data;
+               iter = g_list_next(iter);
+               if (item->id == id) {
+                       pthread_mutex_unlock(&__mutex);
+                       return true;
+               }
+       }
+       pthread_mutex_unlock(&__mutex);
+
+       return false;
+}
+
+static int __pending_item_set_reply_info(int id, struct reply_info_s *info)
+{
+       struct pending_item_s *item;
+       GList *iter;
+
+       pthread_mutex_lock(&__mutex);
+       iter = __pending_list;
+       while (iter) {
+               item = (struct pending_item_s *)iter->data;
+               iter = g_list_next(iter);
+               if (item->id == id) {
+                       item->reply_info = info;
+                       pthread_mutex_unlock(&__mutex);
+                       return 0;
+               }
+       }
+       pthread_mutex_unlock(&__mutex);
+
+       return -1;
+}
+
+static int __pending_item_set_result_info(int id, struct result_info_s *info)
+{
+       struct pending_item_s *item;
+       GList *iter;
+
+       pthread_mutex_lock(&__mutex);
+       iter = __pending_list;
+       while (iter) {
+               item = (struct pending_item_s *)iter->data;
+               iter = g_list_next(iter);
+               if (item->id == id) {
+                       item->result_info = info;
+                       pthread_mutex_unlock(&__mutex);
+                       return 0;
+               }
+       }
+       pthread_mutex_unlock(&__mutex);
+
+       return -1;
+}
 
 static const char *app_control_error_to_string(app_control_error_e error)
 {
@@ -163,10 +454,12 @@ static int app_control_validate(app_control_h app_control)
        return APP_CONTROL_ERROR_NONE;
 }
 
-static int app_control_new_id()
+static int app_control_new_id(void)
 {
-       static int sid = 0;
-       return sid++;
+       static int sid;
+
+       g_atomic_int_inc(&sid);
+       return sid;
 }
 
 int app_control_validate_internal_key(const char *key)
@@ -212,12 +505,28 @@ static void app_control_request_reply_broker(bundle *appsvc_bundle, int appsvc_r
        app_control_result_e result;
        void *user_data;
        app_control_reply_cb reply_cb;
+       struct reply_info_s *info;
+       int ret;
 
        if (appsvc_data == NULL) {
                app_control_error(APP_CONTROL_ERROR_INVALID_PARAMETER, __FUNCTION__, "invalid app_control reply");
                return;
        }
 
+       if (__exist_pending_item(appsvc_request_code)) {
+               info = __create_reply_info(appsvc_bundle,
+                               appsvc_result, appsvc_data);
+               if (info) {
+                       ret = __pending_item_set_reply_info(appsvc_request_code,
+                                       info);
+                       if (ret == 0) {
+                               LOGW("Reply info is pending");
+                               return;
+                       }
+                       __destroy_reply_info(info);
+               }
+       }
+
        if (app_control_create_reply(appsvc_bundle, &reply) != 0) {
                app_control_error(APP_CONTROL_ERROR_INVALID_PARAMETER, __FUNCTION__, "failed to create app_control reply");
                return;
@@ -1032,6 +1341,21 @@ static void app_control_request_result_broker(int request_code, int result,
        app_control_request_context_h request_context;
        app_control_error_e error = APP_CONTROL_ERROR_NONE;
        app_control_h app_control;
+       struct result_info_s *info;
+       int ret;
+
+       if (__exist_pending_item(request_code)) {
+               info = __create_result_info(result, user_data);
+               if (info) {
+                       ret = __pending_item_set_result_info(request_code,
+                                       info);
+                       if (ret == 0) {
+                               LOGW("Result info is pending");
+                               return;
+                       }
+                       __destroy_result_info(info);
+               }
+       }
 
        request_context = (app_control_request_context_h)user_data;
        if (request_context == NULL) {
@@ -1133,14 +1457,16 @@ static int __launch_request_send(struct launch_request_s *req)
                                APP_CONTROL_OPERATION_DEFAULT);
        }
 
+       if (req->request_context)
+               __add_pending_item(req->id);
+
        if (req->result_cb) {
                ret = aul_svc_send_launch_request_for_uid(app_control->data,
-                               app_control->id, reply_cb, result_cb,
+                               req->id, reply_cb, result_cb,
                                req->request_context, getuid());
-
        } else {
                ret = aul_svc_run_service_for_uid(app_control->data,
-                               app_control->id, reply_cb,
+                               req->id, reply_cb,
                                req->request_context, getuid());
        }
 
@@ -1148,6 +1474,8 @@ static int __launch_request_send(struct launch_request_s *req)
                bundle_del(req->app_control->data, BUNDLE_KEY_OPERATION);
 
        if (ret < 0) {
+               if (req->request_context)
+                       __remove_pending_item(req->id);
                return app_control_error(__launch_request_convert_error(ret),
                                __FUNCTION__, NULL);
        }
@@ -1185,14 +1513,17 @@ static int __launch_request_complete(struct launch_request_s *req)
        if (request_context)
                __copy_callee_info(request_context->app_control, app_control);
 
-       if (req->result_cb)
+       if (req->result_cb) {
+               __flush_pending_item(req->id);
                return APP_CONTROL_ERROR_NONE;
+       }
 
        if (!request_context)
                return APP_CONTROL_ERROR_NONE;
 
        __handle_app_started_result(app_control,
                        request_context->reply_cb ? request_context : NULL);
+       __flush_pending_item(req->id);
 
        return APP_CONTROL_ERROR_NONE;
 }
@@ -1226,6 +1557,7 @@ static int __send_launch_request(app_control_h app_control,
                                __FUNCTION__, NULL);
        }
 
+       req.id = __generate_request_id();
        for (i = 0; i < ARRAY_SIZE(handlers); i++) {
                if (handlers[i]) {
                        ret = handlers[i](&req);
@@ -1767,7 +2099,7 @@ static int __launch_request_send_sync(struct launch_request_s *req)
        }
 
        ret = aul_svc_send_launch_request_sync_for_uid(request->data,
-                       request->id, &reply_bundle, &appsvc_result, getuid());
+                       req->id, &reply_bundle, &appsvc_result, getuid());
 
        if (req->implicit_default_operation)
                bundle_del(request->data, BUNDLE_KEY_OPERATION);
@@ -1814,6 +2146,7 @@ static int __send_launch_request_sync(app_control_h request,
                                __FUNCTION__, NULL);
        }
 
+       req.id = __generate_request_id();
        for (i = 0; i < ARRAY_SIZE(handlers); i++) {
                if (handlers[i]) {
                        ret = handlers[i](&req);