mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-20 08:40:16 +00:00
Merge "sched: Don't allow ast_sched_del to deadlock ast_sched_runq from same thread" into 13
This commit is contained in:
@@ -71,20 +71,24 @@ extern "C" {
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief schedule task to get deleted and call unref function
|
* \brief schedule task to get deleted and call unref function
|
||||||
|
*
|
||||||
|
* Only calls unref function if the delete succeeded.
|
||||||
|
*
|
||||||
* \sa AST_SCHED_DEL
|
* \sa AST_SCHED_DEL
|
||||||
* \since 1.6.1
|
* \since 1.6.1
|
||||||
*/
|
*/
|
||||||
#define AST_SCHED_DEL_UNREF(sched, id, refcall) \
|
#define AST_SCHED_DEL_UNREF(sched, id, refcall) \
|
||||||
do { \
|
do { \
|
||||||
int _count = 0; \
|
int _count = 0, _id; \
|
||||||
while (id > -1 && ast_sched_del(sched, id) && ++_count < 10) { \
|
while ((_id = id) > -1 && ast_sched_del(sched, _id) && ++_count < 10) { \
|
||||||
usleep(1); \
|
usleep(1); \
|
||||||
} \
|
} \
|
||||||
if (_count == 10) \
|
if (_count == 10) { \
|
||||||
ast_log(LOG_WARNING, "Unable to cancel schedule ID %d. This is probably a bug (%s: %s, line %d).\n", id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
|
ast_log(LOG_WARNING, "Unable to cancel schedule ID %d. This is probably a bug (%s: %s, line %d).\n", _id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
|
||||||
if (id > -1) \
|
} else if (_id > -1) { \
|
||||||
refcall; \
|
refcall; \
|
||||||
id = -1; \
|
id = -1; \
|
||||||
|
} \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
30
main/sched.c
30
main/sched.c
@@ -118,6 +118,8 @@ struct ast_sched_context {
|
|||||||
struct sched_thread *sched_thread;
|
struct sched_thread *sched_thread;
|
||||||
/*! The scheduled task that is currently executing */
|
/*! The scheduled task that is currently executing */
|
||||||
struct sched *currently_executing;
|
struct sched *currently_executing;
|
||||||
|
/*! Valid while currently_executing is not NULL */
|
||||||
|
pthread_t executing_thread_id;
|
||||||
|
|
||||||
#ifdef SCHED_MAX_CACHE
|
#ifdef SCHED_MAX_CACHE
|
||||||
AST_LIST_HEAD_NOLOCK(, sched) schedc; /*!< Cache of unused schedule structures and how many */
|
AST_LIST_HEAD_NOLOCK(, sched) schedc; /*!< Cache of unused schedule structures and how many */
|
||||||
@@ -627,15 +629,26 @@ int ast_sched_del(struct ast_sched_context *con, int id)
|
|||||||
}
|
}
|
||||||
sched_release(con, s);
|
sched_release(con, s);
|
||||||
} else if (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
|
} else if (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
|
||||||
s = con->currently_executing;
|
if (con->executing_thread_id == pthread_self()) {
|
||||||
s->deleted = 1;
|
/* The scheduled callback is trying to delete itself.
|
||||||
/* Wait for executing task to complete so that caller of ast_sched_del() does not
|
* Not good as that is a deadlock. */
|
||||||
* free memory out from under the task.
|
ast_log(LOG_ERROR,
|
||||||
*/
|
"BUG! Trying to delete sched %d from within the callback %p. "
|
||||||
while (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
|
"Ignoring so we don't deadlock\n",
|
||||||
ast_cond_wait(&s->cond, &con->lock);
|
id, con->currently_executing->callback);
|
||||||
|
ast_log_backtrace();
|
||||||
|
/* We'll return -1 below because s is NULL.
|
||||||
|
* The caller will rightly assume that the unscheduling failed. */
|
||||||
|
} else {
|
||||||
|
s = con->currently_executing;
|
||||||
|
s->deleted = 1;
|
||||||
|
/* Wait for executing task to complete so that the caller of
|
||||||
|
* ast_sched_del() does not free memory out from under the task. */
|
||||||
|
while (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
|
||||||
|
ast_cond_wait(&s->cond, &con->lock);
|
||||||
|
}
|
||||||
|
/* Do not sched_release() here because ast_sched_runq() will do it */
|
||||||
}
|
}
|
||||||
/* Do not sched_release() here because ast_sched_runq() will do it */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DUMP_SCHEDULER
|
#ifdef DUMP_SCHEDULER
|
||||||
@@ -775,6 +788,7 @@ int ast_sched_runq(struct ast_sched_context *con)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
con->currently_executing = current;
|
con->currently_executing = current;
|
||||||
|
con->executing_thread_id = pthread_self();
|
||||||
ast_mutex_unlock(&con->lock);
|
ast_mutex_unlock(&con->lock);
|
||||||
res = current->callback(current->data);
|
res = current->callback(current->data);
|
||||||
ast_mutex_lock(&con->lock);
|
ast_mutex_lock(&con->lock);
|
||||||
|
|||||||
Reference in New Issue
Block a user