diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index ef2f19e..a3a82be 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -777,6 +777,14 @@ static struct dahdi_pvt *iflist = NULL; /*!< Main interface list start */ static struct dahdi_pvt *ifend = NULL; /*!< Main interface list end */ #if defined(HAVE_PRI) +struct doomed_pri { + struct sig_pri_span *pri; + AST_LIST_ENTRY(doomed_pri) list; +}; +static AST_LIST_HEAD_STATIC(doomed_pris, doomed_pri); + +static void pri_destroy_span(struct sig_pri_span *pri); + static struct dahdi_parms_pseudo { int buf_no; /*!< Number of buffers */ int buf_policy; /*!< Buffer policy */ @@ -1110,6 +1118,75 @@ static int analogsub_to_dahdisub(enum analog_sub analogsub) /*! * \internal + * \brief release all members on the doomed pris list + * \since 13.0 + * + * Called priodically by the monitor threads to release spans marked for + * removal. + */ +static void release_doomed_pris(void) { +#ifdef HAVE_PRI + struct doomed_pri *entry; + AST_LIST_HEAD_NOLOCK(doomed_pris, doomed_pri) doomed_list; + + /* Move the global list to our local list. This prevents deadlocks with + * any pri->lock. + */ + AST_LIST_LOCK(&doomed_pris); + doomed_list.first = doomed_pris.first; + doomed_list.last = doomed_pris.last; + doomed_pris.first = doomed_pris.last = NULL; + AST_LIST_UNLOCK(&doomed_pris); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&doomed_list, entry, list) { + AST_LIST_REMOVE_CURRENT(list); + pri_destroy_span(entry->pri); + ast_free(entry); + } + AST_LIST_TRAVERSE_SAFE_END; +#endif +} + +#ifdef HAVE_PRI +/*! + * \internal + * \brief Queue a span for destruction + * \since 13.0 + * + * \param pri the span to destroy + * + * Add a span to the list of spans to be destroyed later on + * by the monitor thread. Allows destroying a span while holding its + * lock. + */ +static void pri_queue_for_destruction(struct sig_pri_span *pri) { + struct doomed_pri *entry; + int len = 0; + + AST_LIST_LOCK(&doomed_pris); + AST_LIST_TRAVERSE(&doomed_pris, entry, list) { + if (entry->pri == pri) { + AST_LIST_UNLOCK(&doomed_pris); + return; + } + len++; + } + entry = ast_calloc(sizeof(struct doomed_pri), 1); + if (!entry) { + /* Nothing useful to do here. Panic? */ + ast_log(LOG_WARNING, "Failed allocating memory for a doomed_pri.\n"); + AST_LIST_UNLOCK(&doomed_pris); + return; + } + entry->pri = pri; + ast_debug(4, "Queue span %d for destruction.\n", pri->span); + AST_LIST_INSERT_TAIL(&doomed_pris, entry, list); + AST_LIST_UNLOCK(&doomed_pris); +} +#endif + +/*! + * \internal * \brief Send a dial string to DAHDI. * \since 12.0.0 * @@ -2650,8 +2727,6 @@ static int sig_pri_tone_to_dahditone(enum sig_pri_tone tone) #endif /* defined(HAVE_PRI) */ #if defined(HAVE_PRI) -static void pri_destroy_span(struct sig_pri_span *pri); - static void my_handle_dchan_exception(struct sig_pri_span *pri, int index) { int x; @@ -2680,7 +2755,7 @@ static void my_handle_dchan_exception(struct sig_pri_span *pri, int index) pri_event_noalarm(pri, index, 0); break; case DAHDI_EVENT_REMOVED: - pri_destroy_span(pri); + pri_queue_for_destruction(pri); break; default: break; @@ -11268,6 +11343,7 @@ static void *do_monitor(void *data) dahdi_destroy_channel_range(doomed->channel, doomed->channel); doomed = NULL; } + release_doomed_pris(); if (!i) { break; }