diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 923970e1a6..0412d66dc1 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -227,7 +227,6 @@ static struct ast_flags globalflags = { 0 }; static pthread_t netthreadid = AST_PTHREADT_NULL; static pthread_t schedthreadid = AST_PTHREADT_NULL; AST_MUTEX_DEFINE_STATIC(sched_lock); -static int sched_halt = 0; static ast_cond_t sched_cond; enum { @@ -448,6 +447,7 @@ static AST_LIST_HEAD_STATIC(registrations, iax2_registry); static int iaxthreadcount = DEFAULT_THREAD_COUNT; static int iaxmaxthreadcount = DEFAULT_MAX_THREAD_COUNT; static int iaxdynamicthreadcount = 0; +static int iaxactivethreadcount = 0; struct iax_rr { int jitter; @@ -690,7 +690,6 @@ struct iax2_thread { char curfunc[80]; #endif int actions; - int halt; pthread_t threadid; int threadnum; struct sockaddr_in iosin; @@ -843,6 +842,7 @@ static void insert_idle_thread(struct iax2_thread *thread) static struct iax2_thread *find_idle_thread(void) { + pthread_attr_t attr; struct iax2_thread *thread = NULL; /* Pop the head of the list off */ @@ -862,7 +862,9 @@ static struct iax2_thread *find_idle_thread(void) thread->type = IAX_TYPE_DYNAMIC; ast_mutex_init(&thread->lock); ast_cond_init(&thread->cond, NULL); - if (ast_pthread_create_background(&thread->threadid, NULL, iax2_process_thread, thread)) { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ast_pthread_create(&thread->threadid, &attr, iax2_process_thread, thread)) { free(thread); thread = NULL; } else { @@ -7587,6 +7589,16 @@ retryowner2: return 1; } +/* Function to clean up process thread if it is cancelled */ +static void iax2_process_thread_cleanup(void *data) +{ + struct iax2_thread *thread = data; + ast_mutex_destroy(&thread->lock); + ast_cond_destroy(&thread->cond); + free(thread); + ast_atomic_dec_and_test(&iaxactivethreadcount); +} + static void *iax2_process_thread(void *data) { struct iax2_thread *thread = data; @@ -7594,6 +7606,8 @@ static void *iax2_process_thread(void *data) struct timespec ts; int put_into_idle = 0; + ast_atomic_fetchadd_int(&iaxactivethreadcount,1); + pthread_cleanup_push(iax2_process_thread_cleanup, data); for(;;) { /* Wait for something to signal us to be awake */ ast_mutex_lock(&thread->lock); @@ -7613,18 +7627,13 @@ static void *iax2_process_thread(void *data) AST_LIST_REMOVE(&dynamic_list, thread, list); iaxdynamicthreadcount--; AST_LIST_UNLOCK(&dynamic_list); - break; + break; /* exiting the main loop */ } } else { ast_cond_wait(&thread->cond, &thread->lock); } ast_mutex_unlock(&thread->lock); - /* If we were signalled, then we are already out of both lists or we are shutting down */ - if (thread->halt) { - break; - } - /* Add ourselves to the active list now */ AST_LIST_LOCK(&active_list); AST_LIST_INSERT_HEAD(&active_list, thread, list); @@ -7660,10 +7669,10 @@ static void *iax2_process_thread(void *data) put_into_idle = 1; } - /* Free our own memory */ - ast_mutex_destroy(&thread->lock); - ast_cond_destroy(&thread->cond); - free(thread); + /* I am exiting here on my own volition, I need to clean up my own data structures + * Assume that I am no longer in any of the lists (idle, active, or dynamic) + */ + pthread_cleanup_pop(1); return NULL; } @@ -8027,17 +8036,16 @@ static void *sched_thread(void *ignore) ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; + pthread_testcancel(); ast_mutex_lock(&sched_lock); ast_cond_timedwait(&sched_cond, &sched_lock, &ts); - if (sched_halt == 1) - break; ast_mutex_unlock(&sched_lock); + pthread_testcancel(); count = ast_sched_runq(sched); if (count >= 20) ast_log(LOG_DEBUG, "chan_iax2: ast_sched_runq ran %d scheduled tasks all at once\n", count); } - ast_mutex_unlock(&sched_lock); return NULL; } @@ -8052,6 +8060,8 @@ static void *network_thread(void *ignore) ast_io_add(io, timingfd, timing_read, AST_IO_IN | AST_IO_PRI, NULL); for(;;) { + pthread_testcancel(); + /* Go through the queue, sending messages which have not yet been sent, and scheduling retransmissions if appropriate */ AST_LIST_LOCK(&iaxq.queue); @@ -8092,6 +8102,8 @@ static void *network_thread(void *ignore) AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&iaxq.queue); + pthread_testcancel(); + if (count >= 20) ast_log(LOG_DEBUG, "chan_iax2: Sent %d queued outbound frames all at once\n", count); @@ -8107,6 +8119,7 @@ static void *network_thread(void *ignore) static int start_network_thread(void) { + pthread_attr_t attr; int threadcount = 0; int x; for (x = 0; x < iaxthreadcount; x++) { @@ -8116,7 +8129,9 @@ static int start_network_thread(void) thread->threadnum = ++threadcount; ast_mutex_init(&thread->lock); ast_cond_init(&thread->cond, NULL); - if (ast_pthread_create_background(&thread->threadid, NULL, iax2_process_thread, thread)) { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ast_pthread_create(&thread->threadid, &attr, iax2_process_thread, thread)) { ast_log(LOG_WARNING, "Failed to create new thread!\n"); free(thread); thread = NULL; @@ -9866,32 +9881,35 @@ static struct ast_cli_entry cli_iax2[] = { static int __unload_module(void) { - pthread_t threadid = AST_PTHREADT_NULL; struct iax2_thread *thread = NULL; int x; + /* Make sure threads do not hold shared resources when they are canceled */ + + /* Grab the sched lock resource to keep it away from threads about to die */ /* Cancel the network thread, close the net socket */ if (netthreadid != AST_PTHREADT_NULL) { + AST_LIST_LOCK(&iaxq.queue); + ast_mutex_lock(&sched_lock); pthread_cancel(netthreadid); + ast_cond_signal(&sched_cond); + ast_mutex_unlock(&sched_lock); /* Release the schedule lock resource */ + AST_LIST_UNLOCK(&iaxq.queue); pthread_join(netthreadid, NULL); } if (schedthreadid != AST_PTHREADT_NULL) { + ast_mutex_lock(&sched_lock); pthread_cancel(schedthreadid); - ast_mutex_lock(&sched_lock); - sched_halt = 1; ast_cond_signal(&sched_cond); - ast_mutex_unlock(&sched_lock); + ast_mutex_unlock(&sched_lock); pthread_join(schedthreadid, NULL); } - + /* Call for all threads to halt */ AST_LIST_LOCK(&idle_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&idle_list, thread, list) { AST_LIST_REMOVE_CURRENT(&idle_list, list); - threadid = thread->threadid; - thread->halt = 1; - signal_condition(&thread->lock, &thread->cond); - pthread_join(threadid, NULL); + pthread_cancel(thread->threadid); } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&idle_list); @@ -9899,10 +9917,7 @@ static int __unload_module(void) AST_LIST_LOCK(&active_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&active_list, thread, list) { AST_LIST_REMOVE_CURRENT(&active_list, list); - threadid = thread->threadid; - thread->halt = 1; - signal_condition(&thread->lock, &thread->cond); - pthread_join(threadid, NULL); + pthread_cancel(thread->threadid); } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&active_list); @@ -9910,16 +9925,17 @@ static int __unload_module(void) AST_LIST_LOCK(&dynamic_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&dynamic_list, thread, list) { AST_LIST_REMOVE_CURRENT(&dynamic_list, list); - threadid = thread->threadid; - thread->halt = 1; - signal_condition(&thread->lock, &thread->cond); - pthread_join(threadid, NULL); + pthread_cancel(thread->threadid); } AST_LIST_TRAVERSE_SAFE_END AST_LIST_UNLOCK(&dynamic_list); AST_LIST_HEAD_DESTROY(&iaxq.queue); + /* Wait for threads to exit */ + while(0 < iaxactivethreadcount) + usleep(10000); + ast_netsock_release(netsock); for (x=0;x