diff --git a/main/sched.c b/main/sched.c index 71b1c30723..f5f768eb98 100644 --- a/main/sched.c +++ b/main/sched.c @@ -118,6 +118,7 @@ struct ast_sched_context { struct sched_thread *sched_thread; /*! The scheduled task that is currently executing */ struct sched *currently_executing; + pthread_t currently_executing_on_thread_id; #ifdef SCHED_MAX_CACHE AST_LIST_HEAD_NOLOCK(, sched) schedc; /*!< Cache of unused schedule structures and how many */ @@ -626,6 +627,18 @@ int ast_sched_del(struct ast_sched_context *con, int id) ast_log(LOG_WARNING,"sched entry %d not in the sched heap?\n", s->sched_id->id); } sched_release(con, s); + } else if (con->currently_executing_on_thread_id == pthread_self()) { + /* We might trample on deleted memory at this point. Not good, + * but it's better than a deadlock. + * Thou shalt not reschedule things from a scheduled callback! + */ + ast_log(LOG_ERROR, + "BUG! Trying to delete sched %d from the same callback %p (sched %d). " + "Ignoring so we don't deadlock\n", + id, con->currently_executing->callback, con->currently_executing->sched_id->id); + ast_log_backtrace(); + /* We'll return -1 below, because s is NULL. The caller + * will rightly assume that the unscheduling failed. */ } else if (con->currently_executing && (id == con->currently_executing->sched_id->id)) { s = con->currently_executing; s->deleted = 1; @@ -771,10 +784,12 @@ int ast_sched_runq(struct ast_sched_context *con) */ con->currently_executing = current; + con->currently_executing_on_thread_id = pthread_self(); ast_mutex_unlock(&con->lock); res = current->callback(current->data); ast_mutex_lock(&con->lock); con->currently_executing = NULL; + con->currently_executing_on_thread_id = 0; ast_cond_signal(¤t->cond); if (res && !current->deleted) {