@@ -3725,19 +3725,6 @@ nc_ch_client_thread(void *arg)
37253725 VRB (session , "Call Home client \"%s\" thread exit." , data -> client_name );
37263726 free (cur_endpt_name );
37273727
3728- /* find the client and clear thread pointer */
3729- if (nc_rwlock_lock (& server_opts .config_lock , NC_RWLOCK_WRITE , NC_CONFIG_LOCK_TIMEOUT , __func__ ) == 1 ) {
3730- client = nc_server_ch_client_get (data -> client_name );
3731- if (client && (client -> thread == data )) {
3732- client -> thread = NULL ;
3733- }
3734- nc_rwlock_unlock (& server_opts .config_lock , __func__ );
3735- }
3736-
3737- free (data -> client_name );
3738- pthread_mutex_destroy (& data -> cond_lock );
3739- pthread_cond_destroy (& data -> cond );
3740- free (data );
37413728 return NULL ;
37423729}
37433730
@@ -3748,12 +3735,15 @@ nc_session_server_ch_client_dispatch_stop(struct nc_ch_client *ch_client)
37483735 struct nc_server_ch_thread_arg * thread_arg ;
37493736 pthread_t tid ;
37503737 enum nc_rwlock_mode config_lock_mode = NC_RWLOCK_WRITE ;
3738+ char * ch_client_name = NULL ;
37513739
37523740 if (!ch_client || !ch_client -> thread ) {
37533741 return 0 ;
37543742 }
37553743
37563744 thread_arg = ch_client -> thread ;
3745+ ch_client_name = strdup (thread_arg -> client_name );
3746+ NC_CHECK_ERRMEM_GOTO (!ch_client_name , rc = 1 , cleanup );
37573747
37583748 /* CH COND LOCK */
37593749 if (nc_mutex_lock (& thread_arg -> cond_lock , NC_CH_COND_LOCK_TIMEOUT , __func__ ) != 1 ) {
@@ -3779,19 +3769,40 @@ nc_session_server_ch_client_dispatch_stop(struct nc_ch_client *ch_client)
37793769 /* wait for the thread to end */
37803770 r = pthread_join (tid , NULL );
37813771 if (r ) {
3782- ERR (NULL , "Joining Call Home client \"%s\" thread failed (%s)." , ch_client -> name , strerror (r ));
3772+ ERR (NULL , "Joining Call Home client \"%s\" thread failed (%s)." , ch_client_name , strerror (r ));
3773+ rc = 1 ;
3774+ goto cleanup ;
3775+ }
3776+
3777+ /* CONFIG WRITE LOCK - re-acquire to clear the thread pointer and free the thread data */
3778+ if (nc_rwlock_lock (& server_opts .config_lock , NC_RWLOCK_WRITE , NC_CONFIG_LOCK_TIMEOUT , __func__ ) != 1 ) {
3779+ /* if we fail, attempt to lock again in cleanup.
3780+ * ch thread data will cause a memory leak, but we should avoid a possible crash this way */
3781+ ERRINT ;
37833782 rc = 1 ;
37843783 goto cleanup ;
37853784 }
3785+ config_lock_mode = NC_RWLOCK_WRITE ;
3786+
3787+ /* clear the thread pointer,
3788+ * ch_client MUST remain valid even though we unlocked config lock,
3789+ * because the caller MUST hold config apply mutex, so no one can change the config and free the client */
3790+ ch_client -> thread = NULL ;
3791+
3792+ /* free the thread data */
3793+ free (thread_arg -> client_name );
3794+ pthread_mutex_destroy (& thread_arg -> cond_lock );
3795+ pthread_cond_destroy (& thread_arg -> cond );
3796+ free (thread_arg );
37863797
37873798cleanup :
37883799 if (config_lock_mode == NC_RWLOCK_NONE ) {
37893800 /* CONFIG LOCK - lock it back if we unlocked it. It MUST succeed, if the caller holds the config apply mutex */
3790- if (nc_rwlock_lock (& server_opts .config_lock , NC_RWLOCK_WRITE , -1 , __func__ ) != 1 ) {
3801+ if (nc_rwlock_lock (& server_opts .config_lock , NC_RWLOCK_WRITE , NC_CONFIG_LOCK_TIMEOUT , __func__ ) != 1 ) {
37913802 ERRINT ;
37923803 }
37933804 }
3794-
3805+ free ( ch_client_name );
37953806 return rc ;
37963807}
37973808
0 commit comments