mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-09 11:28:25 +00:00
Allow AMI action callback to be reentrant.
Fix AMI module reload deadlock regression from ASTERISK-18479 when it tried to fix the race between calling an AMI action callback and unregistering that action. Refixes ASTERISK-13784 broken by ASTERISK-17785 change. Locking the ao2 object guaranteed that there were no active callbacks that mattered when ast_manager_unregister() was called. Unfortunately, this causes the deadlock situation. The patch stops locking the ao2 object to allow multiple threads to invoke the callback re-entrantly. There is no way to guarantee a module unload will not crash because of an active callback. The code attempts to minimize the chance with the registered flag and the maximum 5 second delay before ast_manager_unregister() returns. The trunk version of the patch changes the API to fix the race condition correctly to prevent the module code from unloading from memory while an action callback is active. * Don't hold the lock while calling the AMI action callback. (closes issue ASTERISK-19487) Reported by: Philippe Lindheimer Review: https://reviewboard.asterisk.org/r/1818/ Review: https://reviewboard.asterisk.org/r/1820/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@359979 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -160,6 +160,8 @@ struct manager_action {
|
|||||||
* function and unregestring the AMI action object.
|
* function and unregestring the AMI action object.
|
||||||
*/
|
*/
|
||||||
unsigned int registered:1;
|
unsigned int registered:1;
|
||||||
|
/*! Number of active func() calls in progress. */
|
||||||
|
unsigned int active_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief External routines may register/unregister manager callbacks this way
|
/*! \brief External routines may register/unregister manager callbacks this way
|
||||||
|
|||||||
@@ -1935,7 +1935,11 @@ int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
|
|||||||
|
|
||||||
ao2_lock(act_found);
|
ao2_lock(act_found);
|
||||||
if (act_found->registered && act_found->func) {
|
if (act_found->registered && act_found->func) {
|
||||||
|
++act_found->active_count;
|
||||||
|
ao2_unlock(act_found);
|
||||||
ret = act_found->func(&s, &m);
|
ret = act_found->func(&s, &m);
|
||||||
|
ao2_lock(act_found);
|
||||||
|
--act_found->active_count;
|
||||||
} else {
|
} else {
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
@@ -4635,8 +4639,12 @@ static int process_message(struct mansession *s, const struct message *m)
|
|||||||
ao2_lock(act_found);
|
ao2_lock(act_found);
|
||||||
if (act_found->registered && act_found->func) {
|
if (act_found->registered && act_found->func) {
|
||||||
ast_debug(1, "Running action '%s'\n", act_found->action);
|
ast_debug(1, "Running action '%s'\n", act_found->action);
|
||||||
|
++act_found->active_count;
|
||||||
|
ao2_unlock(act_found);
|
||||||
ret = act_found->func(s, m);
|
ret = act_found->func(s, m);
|
||||||
acted = 1;
|
acted = 1;
|
||||||
|
ao2_lock(act_found);
|
||||||
|
--act_found->active_count;
|
||||||
}
|
}
|
||||||
ao2_unlock(act_found);
|
ao2_unlock(act_found);
|
||||||
}
|
}
|
||||||
@@ -5144,6 +5152,8 @@ int ast_manager_unregister(char *action)
|
|||||||
AST_RWLIST_UNLOCK(&actions);
|
AST_RWLIST_UNLOCK(&actions);
|
||||||
|
|
||||||
if (cur) {
|
if (cur) {
|
||||||
|
time_t now;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We have removed the action object from the container so we
|
* We have removed the action object from the container so we
|
||||||
* are no longer in a hurry.
|
* are no longer in a hurry.
|
||||||
@@ -5152,6 +5162,23 @@ int ast_manager_unregister(char *action)
|
|||||||
cur->registered = 0;
|
cur->registered = 0;
|
||||||
ao2_unlock(cur);
|
ao2_unlock(cur);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait up to 5 seconds for any active invocations to complete
|
||||||
|
* before returning. We have to wait instead of blocking
|
||||||
|
* because we may be waiting for ourself to complete.
|
||||||
|
*/
|
||||||
|
now = time(NULL);
|
||||||
|
while (cur->active_count) {
|
||||||
|
if (5 <= time(NULL) - now) {
|
||||||
|
ast_debug(1,
|
||||||
|
"Unregister manager action %s timed out waiting for %d active instances to complete\n",
|
||||||
|
action, cur->active_count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
|
||||||
ao2_t_ref(cur, -1, "action object removed from list");
|
ao2_t_ref(cur, -1, "action object removed from list");
|
||||||
ast_verb(2, "Manager unregistered action %s\n", action);
|
ast_verb(2, "Manager unregistered action %s\n", action);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user