]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-7593 --resolve add locking keyed on device name around any database updates addin...
authorNathan Neulinger <nneul@neulinger.org>
Mon, 1 Jun 2015 17:22:08 +0000 (12:22 -0500)
committerNathan Neulinger <nneul@neulinger.org>
Mon, 1 Jun 2015 17:23:45 +0000 (12:23 -0500)
src/mod/endpoints/mod_skinny/mod_skinny.c
src/mod/endpoints/mod_skinny/mod_skinny.h
src/mod/endpoints/mod_skinny/skinny_server.c

index 5925abebe47d310aeb63b6a3fae35fc12fee121d..11a46a13acce7dfe69060c2a515dde7a9cea2c41 100644 (file)
@@ -1462,6 +1462,86 @@ static int flush_listener_callback(void *pArg, int argc, char **argv, char **col
        return 0;
 }
 
+void skinny_lock_device_name(listener_t *listener, char *device_name)
+{
+       switch_time_t started = 0;
+       unsigned int elapsed = 0;
+       device_name_lock_t *dnl;
+
+       if ( listener->profile->debug >= 9 ) {
+               skinny_log_l(listener, SWITCH_LOG_DEBUG, "lock device name '%s'\n", device_name);
+       }
+
+       started = switch_micro_time_now();
+
+       /* global mutex on hash operations */
+       switch_mutex_lock(listener->profile->device_name_lock_mutex);
+
+       dnl = (device_name_lock_t *) switch_core_hash_find(listener->profile->device_name_lock_hash, device_name);
+       if ( ! dnl ) {
+               if ( listener->profile->debug >= 9 ) {
+                       skinny_log_l(listener, SWITCH_LOG_DEBUG, "creating device name lock for device name '%s'\n", device_name);
+               }
+               dnl = switch_core_alloc(listener->profile->pool, sizeof(*dnl));
+               switch_mutex_init(&dnl->flag_mutex, SWITCH_MUTEX_NESTED, listener->profile->pool);
+               switch_core_hash_insert(listener->profile->device_name_lock_hash, device_name, dnl);
+       }
+
+       switch_mutex_unlock(listener->profile->device_name_lock_mutex);
+
+       if ( listener->profile->debug >= 9 ) {
+               skinny_log_l(listener, SWITCH_LOG_DEBUG, "setting device name lock for device name '%s'\n", device_name);
+       }
+       switch_set_flag_locked(dnl, DNLFLAG_INUSE);
+
+       if ((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > 5) {
+               skinny_log_l(listener, SWITCH_LOG_DEBUG, "device name lock took more than 5ms for '%s' (%d)\n", device_name, elapsed);
+       }
+
+       if ( listener->profile->debug >= 9 ) {
+               skinny_log_l(listener, SWITCH_LOG_DEBUG, "locked device name '%s'\n", device_name);
+       }
+}
+
+void skinny_unlock_device_name(listener_t *listener, char *device_name)
+{
+       switch_time_t started = 0;
+       unsigned int elapsed = 0;
+       device_name_lock_t *dnl;
+
+       if ( listener->profile->debug >= 9 ) {
+               skinny_log_l(listener, SWITCH_LOG_DEBUG, "unlock device name '%s'\n", device_name);
+       }
+
+       started = switch_micro_time_now();
+
+       /* global mutex on hash operations */
+       switch_mutex_lock(listener->profile->device_name_lock_mutex);
+       dnl = (device_name_lock_t *) switch_core_hash_find(listener->profile->device_name_lock_hash, device_name);
+       switch_mutex_unlock(listener->profile->device_name_lock_mutex);
+
+       if ( ! dnl ) {
+               skinny_log_l(listener, SWITCH_LOG_WARNING, "request to unlock and no lock structure for '%s'\n", device_name);
+               /* since it didn't exist, nothing to unlock, don't bother creating structure now */
+       } else {
+               if ( listener->profile->debug >= 9 ) {
+                       skinny_log_l(listener, SWITCH_LOG_DEBUG, "clearing device name lock on '%s'\n", device_name);
+               }
+               switch_clear_flag_locked(dnl, DNLFLAG_INUSE);
+       }
+
+       /* Should we clean up the lock structure here, or does it ever get reclaimed? I don't think memory is released
+               so attempting to clear it up here likely would just result in a leak. */
+
+       if ((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > 5) {
+               skinny_log_l(listener, SWITCH_LOG_DEBUG, "device name unlock took more than 5ms for '%s' (%d)\n", device_name, elapsed);
+       }
+
+       if ( listener->profile->debug >= 9 ) {
+               skinny_log_l(listener, SWITCH_LOG_DEBUG, "unlocked device name '%s'\n", device_name);
+       }
+}
+
 void skinny_clean_device_from_db(listener_t *listener, char *device_name)
 {
        if(!zstr(device_name)) {
@@ -1577,7 +1657,9 @@ static void flush_listener(listener_t *listener)
                        switch_safe_free(sql);
                }
 
+               skinny_lock_device_name(listener, listener->device_name);
                skinny_clean_listener_from_db(listener);
+               skinny_unlock_device_name(listener, listener->device_name);
 
                strcpy(listener->device_name, "");
        }
@@ -1656,7 +1738,7 @@ switch_status_t kill_listener(listener_t *listener, void *pvt)
 {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Killing listener %s:%d.\n",
                        listener->device_name, listener->device_instance);
-       switch_clear_flag(listener, LFLAG_RUNNING);
+       switch_clear_flag_locked(listener, LFLAG_RUNNING);
        close_socket(&listener->sock, listener->profile);
        return SWITCH_STATUS_SUCCESS;
 }
@@ -1738,11 +1820,12 @@ static void *SWITCH_THREAD_FUNC listener_run(switch_thread_t *thread, void *obj)
                skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, "Connection Open\n");
        }
 
+       listener->connect_time = switch_epoch_time_now(NULL);
+
        switch_set_flag_locked(listener, LFLAG_RUNNING);
        keepalive_listener(listener, NULL);
        add_listener(listener);
 
-
        while (listener_is_ready(listener)) {
                status = skinny_read_packet(listener, &request);
 
@@ -2114,6 +2197,8 @@ static switch_status_t load_skinny_config(void)
                                switch_mutex_init(&profile->sock_mutex, SWITCH_MUTEX_NESTED, profile->pool);
                                switch_mutex_init(&profile->flag_mutex, SWITCH_MUTEX_NESTED, profile->pool);
 
+                               switch_mutex_init(&profile->device_name_lock_mutex, SWITCH_MUTEX_NESTED, profile->pool);
+
                                for (param = switch_xml_child(xsettings, "param"); param; param = param->next) {
                                        char *var = (char *) switch_xml_attr_soft(param, "name");
                                        char *val = (char *) switch_xml_attr_soft(param, "value");
@@ -2231,6 +2316,8 @@ static switch_status_t load_skinny_config(void)
                                        continue;
                                }
 
+                               /* Device Name Locks */
+                               switch_core_hash_init(&profile->device_name_lock_hash);
 
                                /* Device types */
                                switch_core_hash_init(&profile->device_type_params_hash);
index 425710dae470c0f0e41ef96aacce401c2240932b..b0f6abf1be3a9f74cdb4183e581fa01070d68528 100644 (file)
@@ -127,6 +127,9 @@ struct skinny_profile {
        int non_blocking;
        switch_hash_t *soft_key_set_sets_hash;
        switch_hash_t *device_type_params_hash;
+       /* lock on device names for multiple connection handling */
+       switch_mutex_t *device_name_lock_mutex;
+       switch_hash_t *device_name_lock_hash;
        /* extensions */
        char *ext_voicemail;
        char *ext_redial;
@@ -169,6 +172,23 @@ typedef enum {
        SKINNY_ACTION_WAIT
 } skinny_action_t;
 
+
+
+/*****************************************************************************/
+/* DEVICE NAME LOCK TYPES */
+/*****************************************************************************/
+typedef enum {
+       DNLFLAG_INUSE = (1 << 0),
+} device_name_lock_flag_t;
+
+struct device_name_lock {
+       char device_name[16];
+       switch_mutex_t *flag_mutex;
+       uint32_t flags;
+};
+
+typedef struct device_name_lock device_name_lock_t;
+
 /*****************************************************************************/
 /* LISTENERS TYPES */
 /*****************************************************************************/
@@ -197,6 +217,7 @@ struct listener {
        switch_mutex_t *flag_mutex;
        uint32_t flags;
        time_t expire_time;
+       time_t connect_time;
        switch_time_t digit_timeout_time;
        struct listener *next;
        char *ext_voicemail;
@@ -301,6 +322,12 @@ switch_status_t keepalive_listener(listener_t *listener, void *pvt);
 void skinny_clean_listener_from_db(listener_t *listener);
 void skinny_clean_device_from_db(listener_t *listener, char *device_name);
 
+/*****************************************************************************/
+/* DEVICE NAME LOCK FUNCTIONS */
+/*****************************************************************************/
+void skinny_lock_device_name(listener_t *listener, char *device_name);
+void skinny_unlock_device_name(listener_t *listener, char *device_name);
+
 /*****************************************************************************/
 /* CHANNEL FUNCTIONS */
 /*****************************************************************************/
index 5a581de463d2b441b76ef2b05ff16bdc00eb1bd7..7d4facfcf77cc3cd7cafce4bf7aaa1af40b3013b 100644 (file)
@@ -1113,12 +1113,17 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r
        switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "action", "skinny-auth");
 
        /* clean up all traces before adding to database */
+       skinny_lock_device_name(listener, request->data.reg.device_name);
        skinny_clean_device_from_db(listener, request->data.reg.device_name);
 
        if (switch_xml_locate_user("id", request->data.reg.device_name, profile->domain, "", &xroot, &xdomain, &xuser, &xgroup, params) != SWITCH_STATUS_SUCCESS) {
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Can't find device [%s@%s]\n"
                                "You must define a domain called '%s' in your directory and add a user with id=\"%s\".\n"
                                , request->data.reg.device_name, profile->domain, profile->domain, request->data.reg.device_name);
+
+               /* unlock before trying to send response in case socket blocks */
+               skinny_unlock_device_name(listener, request->data.reg.device_name);
+
                send_register_reject(listener, "Device not found");
                status =  SWITCH_STATUS_FALSE;
                goto end;
@@ -1135,6 +1140,10 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
                                "Device %s:%d is already registered on another listener.\n",
                                request->data.reg.device_name, request->data.reg.instance);
+
+               /* unlock before trying to send response in case socket blocks */
+               skinny_unlock_device_name(listener, request->data.reg.device_name);
+
                send_register_reject(listener, "Device is already registered on another listener");
                status =  SWITCH_STATUS_FALSE;
                goto end;
@@ -1156,11 +1165,12 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r
                switch_safe_free(sql);
        }
 
-
        switch_copy_string(listener->device_name, request->data.reg.device_name, 16);
        listener->device_instance = request->data.reg.instance;
        listener->device_type = request->data.reg.device_type;
 
+       skinny_unlock_device_name(listener, request->data.reg.device_name);
+
        xskinny = switch_xml_child(xuser, "skinny");
        if (xskinny) {
                if ((xparams = switch_xml_child(xskinny, "params"))) {