Fix logical deadlock issue 12/265512/10 accepted/tizen/unified/20211216.155827 submit/tizen/20211215.063645
authorNishant Chaprana <n.chaprana@samsung.com>
Thu, 21 Oct 2021 06:45:10 +0000 (12:15 +0530)
committerNishant Chaprana <n.chaprana@samsung.com>
Wed, 24 Nov 2021 12:36:39 +0000 (18:06 +0530)
Change-Id: I711ffa1aab15b0e1301c81217236b080b24bba6e
Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
include/net_connection_private.h
src/connection.c
src/libnetwork.c

index 0e077b6..350a26a 100755 (executable)
@@ -133,6 +133,7 @@ typedef struct _connection_handle_s {
        void *reset_user_data;
 
        void *network_info_handle;
+       int refcount;
 } connection_handle_s;
 
 char *_connection_vconf_get_str(connection_handle_s *conn_handle, const char *key);
@@ -222,6 +223,8 @@ void _connection_unlock(void);
 
 bool _connection_check_handle_validity(connection_h connection);
 void *_connection_get_default_handle(void);
+void _connection_handle_ref(connection_handle_s *handle);
+void _connection_handle_unref(connection_handle_s *handle);
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 37856ac..7e2c72e 100755 (executable)
@@ -90,67 +90,154 @@ void *_connection_get_default_handle(void)
        return NULL;
 }
 
+void _connection_handle_ref(connection_handle_s *handle)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "%p ref %d",
+                       handle, handle->refcount + 1);
+
+       __sync_fetch_and_add(&handle->refcount, 1);
+}
+
+void _connection_handle_unref(connection_handle_s *handle)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "%p ref %d",
+                       handle, handle->refcount - 1);
+
+       if (__sync_fetch_and_sub(&handle->refcount, 1) != 1)
+               return;
+
+       conn_handle_list = g_slist_remove(conn_handle_list, handle);
+       _connection_libnet_deinit(handle, (conn_handle_list == NULL));
+
+       g_free(handle);
+}
+
+static void __connection_dummy_type_changed_cb(connection_type_e type, void* user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
+}
+
 static void __connection_set_type_changed_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
        conn_handle->type_changed_user_data = user_data;
-       conn_handle->type_changed_callback = callback;
+       if (callback != NULL)
+               conn_handle->type_changed_callback = callback;
+       else
+               conn_handle->type_changed_callback = __connection_dummy_type_changed_cb;
+}
+
+static void __connection_dummy_address_changed_cb(const char* ipv4_address,
+                       const char* ipv6_address, void* user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
 }
 
 static void __connection_set_ip_changed_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
        conn_handle->ip_changed_user_data = user_data;
-       conn_handle->ip_changed_callback = callback;
+       if (callback != NULL)
+               conn_handle->ip_changed_callback = callback;
+       else
+               conn_handle->ip_changed_callback = __connection_dummy_address_changed_cb;
 }
 
 static void __connection_set_proxy_changed_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
        conn_handle->proxy_changed_user_data = user_data;
-       conn_handle->proxy_changed_callback = callback;
+       if (callback != NULL)
+               conn_handle->proxy_changed_callback = callback;
+       else
+               conn_handle->proxy_changed_callback = __connection_dummy_address_changed_cb;
+}
+
+static void __connection_dummy_internet_state_changed_cb(connection_internet_state_e state,
+                       void* user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
 }
 
 static void __connection_set_internet_state_changed_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
        conn_handle->internet_state_changed_user_data = user_data;
-       conn_handle->internet_state_changed_callback = callback;
+       if (callback != NULL)
+               conn_handle->internet_state_changed_callback = callback;
+       else
+               conn_handle->internet_state_changed_callback = __connection_dummy_internet_state_changed_cb;
+}
+
+static void __connection_dummy_ethernet_cable_state_changed_cb(connection_ethernet_cable_state_e state,
+                       void* user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
 }
 
 static void __connection_set_ethernet_cable_state_changed_cb(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
-       conn_handle->ethernet_cable_state_changed_callback = callback;
        conn_handle->ethernet_cable_state_changed_user_data = user_data;
+       if (callback != NULL)
+               conn_handle->ethernet_cable_state_changed_callback = callback;
+       else
+               conn_handle->ethernet_cable_state_changed_callback = __connection_dummy_ethernet_cable_state_changed_cb;
+}
+
+static void __connection_dummy_set_default_cb(connection_error_e result, void* user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
 }
 
 static void __connection_set_default_cellular_service_profile_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
-       conn_handle->set_default_callback = callback;
        conn_handle->set_default_user_data = user_data;
+       if (callback != NULL)
+               conn_handle->set_default_callback = callback;
+       else
+               conn_handle->set_default_callback = __connection_dummy_set_default_cb;
+}
+
+static void __connection_dummy_opened_cb(connection_error_e result, void* user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
 }
 
 static void __connection_open_profile_set_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
-       conn_handle->opened_callback = callback;
        conn_handle->opened_user_data = user_data;
+       if (callback != NULL)
+               conn_handle->opened_callback = callback;
+       else
+               conn_handle->opened_callback = __connection_dummy_opened_cb;
+}
+
+static void __connection_dummy_closed_cb(connection_error_e result, void* user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
 }
 
 static void __connection_close_profile_set_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
-       conn_handle->closed_callback = callback;
        conn_handle->closed_user_data = user_data;
+       if (callback != NULL)
+               conn_handle->closed_callback = callback;
+       else
+               conn_handle->closed_callback = __connection_dummy_closed_cb;
 }
 
 static void __connection_reset_profile_set_callback(connection_handle_s *conn_handle,
                        void *callback, void *user_data)
 {
-       conn_handle->reset_callback = callback;
        conn_handle->reset_user_data = user_data;
+       if (callback != NULL)
+               conn_handle->reset_callback = callback;
+       else
+               conn_handle->reset_callback = __connection_dummy_opened_cb;
 }
 //LCOV_EXCL_STOP
 
@@ -193,6 +280,7 @@ EXPORT_API int connection_create(connection_h *connection)
        }
 
        conn_handle_list = g_slist_prepend(conn_handle_list, *connection);
+       _connection_handle_ref(*connection);
 
        CONN_UNLOCK;
        return CONNECTION_ERROR_NONE;
@@ -218,10 +306,7 @@ EXPORT_API int connection_destroy(connection_h connection)
        __connection_set_internet_state_changed_callback(connection, NULL, NULL);
        __connection_set_ethernet_cable_state_changed_cb(connection, NULL, NULL);
 
-       conn_handle_list = g_slist_remove(conn_handle_list, connection);
-       _connection_libnet_deinit(connection, (conn_handle_list == NULL));
-
-       g_free(connection);
+       _connection_handle_unref(connection);
 
        CONN_UNLOCK;
        return CONNECTION_ERROR_NONE;
index 6ae47e0..7541231 100755 (executable)
@@ -215,23 +215,33 @@ int _connection_vconf_get_bool(connection_handle_s *conn_handle, const char *key
 
 static void __libnet_state_changed_cb(char *profile_name, connection_profile_state_e state)
 {
+       CONN_LOCK;
        struct _profile_cb_s *cb_info;
 
-       if (profile_name == NULL)
+       if (profile_name == NULL) {
+               CONN_UNLOCK;
                return;
+       }
 
        cb_info = g_hash_table_lookup(profile_cb_table, profile_name);
-       if (cb_info == NULL)
+       if (cb_info == NULL) {
+               CONN_UNLOCK;
                return;
+       }
 
-       if (cb_info->state == state)
+       if (cb_info->state == state) {
+               CONN_UNLOCK;
                return;
+       }
 
        cb_info->state = state;
 
-       if (state < 0 || cb_info->callback == NULL)
+       if (state < 0 || cb_info->callback == NULL) {
+               CONN_UNLOCK;
                return;
+       }
 
+       CONN_UNLOCK;
        cb_info->callback(cb_info->state, cb_info->user_data);
 }
 
@@ -270,11 +280,17 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
 
                if (is_requested) {
                        if (conn_handle->opened_callback) {
+
+                               _connection_handle_ref(conn_handle);
+                               CONN_UNLOCK;
+
                                conn_handle->opened_callback(result,
                                        conn_handle->opened_user_data);
 
+                               CONN_LOCK;
                                conn_handle->opened_callback = NULL;
                                conn_handle->opened_user_data = NULL;
+                               _connection_handle_unref(conn_handle);
                        }
                }
 
@@ -283,17 +299,31 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                case NET_ERR_ACTIVE_CONNECTION_EXISTS:
                        CONNECTION_LOG(CONNECTION_INFO, "Successfully open connection");
 
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        __libnet_state_changed_cb(event_cb->profile_name, CONNECTION_PROFILE_STATE_CONNECTED);
+
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
                        CONN_UNLOCK;
+
                        return;
                default:
                        CONNECTION_LOG(CONNECTION_ERROR, "Failed to open connection[%s]",
                                                __libnet_convert_cp_error_type_to_string(result));
                }
 
+               _connection_handle_ref(conn_handle);
+               CONN_UNLOCK;
+
                __libnet_state_changed_cb(event_cb->profile_name, CONNECTION_PROFILE_STATE_DISCONNECTED);
 
-               break;
+               CONN_LOCK;
+               _connection_handle_unref(conn_handle);
+               CONN_UNLOCK;
+
+               return;
        case NET_EVENT_CLOSE_RSP:
                is_requested = true;
                /* fall through */
@@ -305,11 +335,17 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
 
                if (is_requested) {
                        if (conn_handle->closed_callback) {
+
+                               _connection_handle_ref(conn_handle);
+                               CONN_UNLOCK;
+
                                conn_handle->closed_callback(result,
                                        conn_handle->closed_user_data);
 
+                               CONN_LOCK;
                                conn_handle->closed_callback = NULL;
                                conn_handle->closed_user_data = NULL;
+                               _connection_handle_unref(conn_handle);
                        }
                }
 
@@ -317,8 +353,15 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                case NET_ERR_NONE:
                        CONNECTION_LOG(CONNECTION_INFO, "Successfully closed connection");
 
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        __libnet_state_changed_cb(event_cb->profile_name, CONNECTION_PROFILE_STATE_DISCONNECTED);
+
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
                        CONN_UNLOCK;
+
                        return;
                default:
                        CONNECTION_LOG(CONNECTION_ERROR, "Failed to close connection[%s]",
@@ -340,43 +383,82 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                CONNECTION_LOG(CONNECTION_INFO, "state: %s", __libnet_convert_cp_state_to_string(cp_state));
                SECURE_CONNECTION_LOG(CONNECTION_INFO, "profile name: %s", event_cb->profile_name);
 
+               _connection_handle_ref(conn_handle);
+               CONN_UNLOCK;
+
                __libnet_state_changed_cb(event_cb->profile_name, cp_state);
 
-               break;
+               CONN_LOCK;
+               _connection_handle_unref(conn_handle);
+               CONN_UNLOCK;
+
+               return;
        case NET_EVENT_CELLULAR_SET_DEFAULT_RSP:
                result = __libnet_convert_to_cp_error_type(event_cb->Error);
                CONNECTION_LOG(CONNECTION_INFO, "Got set default profile RSP %d", result);
                if (conn_handle->set_default_callback) {
+
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->set_default_callback(result,
                                conn_handle->set_default_user_data);
 
+                       CONN_LOCK;
                        conn_handle->set_default_callback = NULL;
                        conn_handle->set_default_user_data = NULL;
+                       _connection_handle_unref(conn_handle);
                }
                break;
        case NET_EVENT_CELLULAR_RESET_DEFAULT_RSP:
                result = __libnet_convert_to_cp_error_type(event_cb->Error);
                CONNECTION_LOG(CONNECTION_INFO, "Got reset default profile RSP %d", result);
                if (conn_handle->reset_callback) {
+
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->reset_callback(result,
                                conn_handle->reset_user_data);
 
+                       CONN_LOCK;
                        conn_handle->reset_callback = NULL;
                        conn_handle->reset_user_data = NULL;
+                       _connection_handle_unref(conn_handle);
                }
                break;
        case NET_EVENT_ETHERNET_CABLE_ATTACHED:
                CONNECTION_LOG(CONNECTION_INFO, "Got Ethernet cable Attached Indication\n");
                if (conn_handle->ethernet_cable_state_changed_callback) {
+
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->ethernet_cable_state_changed_callback(CONNECTION_ETHERNET_CABLE_ATTACHED,
                                conn_handle->ethernet_cable_state_changed_user_data);
+
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
+                       CONN_UNLOCK;
+
+                       return;
                }
                break;
        case NET_EVENT_ETHERNET_CABLE_DETACHED:
                CONNECTION_LOG(CONNECTION_INFO, "Got Ethernet cable detached Indication\n");
                if (conn_handle->ethernet_cable_state_changed_callback) {
+
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->ethernet_cable_state_changed_callback(CONNECTION_ETHERNET_CABLE_DETACHED,
                                conn_handle->ethernet_cable_state_changed_user_data);
+
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
+                       CONN_UNLOCK;
+
+                       return;
                }
                break;
        case NET_EVENT_NETWORK_TYPE_CHANGED:
@@ -406,8 +488,17 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                                break;
                        }
 
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->type_changed_callback(type,
                                conn_handle->type_changed_user_data);
+
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
+                       CONN_UNLOCK;
+
+                       return;
                }
                break;
        case NET_EVENT_IPV4_ADDRESS_CHANGED:
@@ -423,11 +514,19 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                                CONNECTION_LOG(CONNECTION_ERROR, //LCOV_EXCL_LINE
                                                "vconf_get_str(VCONFKEY_NETWORK_IP6) failed");
 
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->ip_changed_callback(ipv4_addr, ipv6_addr,
                                conn_handle->ip_changed_user_data);
 
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
+                       CONN_UNLOCK;
+
                        g_free(ipv4_addr);
                        g_free(ipv6_addr);
+                       return;
                }
                break;
        case NET_EVENT_IPV6_ADDRESS_CHANGED:
@@ -443,11 +542,19 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                                CONNECTION_LOG(CONNECTION_ERROR, //LCOV_EXCL_LINE
                                                "vconf_get_str(VCONFKEY_NETWORK_IP) failed");
 
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->ip_changed_callback(ipv4_addr, ipv6_addr,
                                conn_handle->ip_changed_user_data);
 
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
+                       CONN_UNLOCK;
+
                        g_free(ipv4_addr);
                        g_free(ipv6_addr);
+                       return;
                }
                break;
        case NET_EVENT_PROXY_ADDRESS_CHANGED:
@@ -455,8 +562,18 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                char *proxy_addr = (char *)event_cb->data;
 
                if (conn_handle->proxy_changed_callback) {
+
+                       _connection_handle_ref(conn_handle);
+                       CONN_UNLOCK;
+
                        conn_handle->proxy_changed_callback(proxy_addr, NULL,
                                conn_handle->proxy_changed_user_data);
+
+                       CONN_LOCK;
+                       _connection_handle_unref(conn_handle);
+                       CONN_UNLOCK;
+
+                       return;
                }
                break;
        case NET_EVENT_INTERNET_ONLINE_IND:
@@ -472,9 +589,18 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
                        rv = net_get_active_net_info(conn_handle->network_info_handle, &active_profile);
 
                        if (rv == NET_ERR_NO_SERVICE && event_cb->event == NET_EVENT_INTERNET_OFFLINE_IND) {
+
+                               _connection_handle_ref(conn_handle);
+                               CONN_UNLOCK;
+
                                conn_handle->internet_state_changed_callback(CONNECTION_INTERNET_STATE_OFFLINE,
                                        conn_handle->internet_state_changed_user_data); //LCOV_EXCL_LINE
-                               break;
+
+                               CONN_LOCK;
+                               _connection_handle_unref(conn_handle);
+                               CONN_UNLOCK;
+
+                               return;
                        } else if (rv == NET_ERR_ACCESS_DENIED) {
                                CONNECTION_LOG(CONNECTION_ERROR, "Access denied"); //LCOV_EXCL_LINE
                                break;
@@ -485,13 +611,35 @@ static void __libnet_evt_cb(net_event_info_t *event_cb, void *user_data)
 
                        if (event_cb->event == NET_EVENT_INTERNET_ONLINE_IND) {
                                if (active_profile.profile_state == NET_STATE_TYPE_ONLINE &&
-                                               active_profile.profile_type == *device_type)
+                                               active_profile.profile_type == *device_type) {
+
+                                       _connection_handle_ref(conn_handle);
+                                       CONN_UNLOCK;
+
                                        conn_handle->internet_state_changed_callback(CONNECTION_INTERNET_STATE_ONLINE,
                                                        conn_handle->internet_state_changed_user_data);
+
+                                       CONN_LOCK;
+                                       _connection_handle_unref(conn_handle);
+                                       CONN_UNLOCK;
+
+                                       return;
+                               }
                        } else {
-                               if (active_profile.profile_state != NET_STATE_TYPE_ONLINE)
+                               if (active_profile.profile_state != NET_STATE_TYPE_ONLINE) {
+
+                                       _connection_handle_ref(conn_handle);
+                                       CONN_UNLOCK;
+
                                        conn_handle->internet_state_changed_callback(CONNECTION_INTERNET_STATE_OFFLINE,
                                                        conn_handle->internet_state_changed_user_data);
+
+                                       CONN_LOCK;
+                                       _connection_handle_unref(conn_handle);
+                                       CONN_UNLOCK;
+
+                                       return;
+                               }
                        }
                }
                break;
@@ -562,6 +710,21 @@ static void __libnet_copy_default_profile(net_profile_info_t **dest, struct _pro
 }
 //LCOV_EXCL_STOP
 
+static void __connection_dummy_profile_state_changed_cb(connection_profile_state_e state,
+               void *user_data)
+{
+       CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
+}
+
+static int __profile_cb_table_value_destroy(gpointer data)
+{
+       struct _profile_cb_s *profile_cb_data = (struct _profile_cb_s *)data;
+
+       g_free(profile_cb_data);
+
+       return G_SOURCE_REMOVE;
+}
+
 int _connection_libnet_init(connection_handle_s *conn_handle)
 {
        int rv;
@@ -572,7 +735,7 @@ int _connection_libnet_init(connection_handle_s *conn_handle)
                return rv;
 
        if (profile_cb_table == NULL)
-               profile_cb_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+               profile_cb_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 
        if (!in_container && access(CONTAINER_FILE, F_OK) == 0)
                in_container = TRUE;
@@ -1396,11 +1559,31 @@ bool _connection_libnet_add_to_profile_cb_list(connection_profile_h profile,
 bool _connection_libnet_remove_from_profile_cb_list(connection_profile_h profile)
 {
        net_profile_info_t *profile_info = profile;
+       struct _profile_cb_s *profile_cb_info;
+
+       profile_cb_info = g_hash_table_lookup(profile_cb_table, profile_info->profile_name);
+       if (profile_cb_info == NULL)
+               return false;
 
-       if (g_hash_table_remove(profile_cb_table, profile_info->profile_name) == TRUE)
-               return true;
+       g_hash_table_remove(profile_cb_table, profile_info->profile_name);
 
-       return false; //LCOV_EXCL_LINE
+       /**
+        * Intentional:
+        * Marking callback to dummy callback.
+        *
+        * Reason:
+        * To avoid race condition between calling profile_state_changed_cb()
+        * and _connection_libnet_remove_from_profile_cb_list()
+        * in multi-threaded environment.
+        */
+       profile_cb_info->callback = __connection_dummy_profile_state_changed_cb;
+       profile_cb_info->user_data = NULL;
+       profile_cb_info->state = -1;
+
+       /* Timer to free hash table entry */
+       g_timeout_add(200, __profile_cb_table_value_destroy, profile_cb_info);
+
+       return true;
 }
 
 int _connection_libnet_set_statistics(connection_handle_s *conn_handle,