stasis: Don't hold app_registry and session locks unnecessarily

resource_events:app_handler() was locking the session, then
attempting to determine if the app had debug enabled which
locked the app_registry container.  res_stasis:__stasis_app_register
was locking the app_registry container then calling app_update
which caused app_handler (which locks the session) to run.
The result was a deadlock.

* Updated resource_events:app_handler() to determine if debug was
  set (which locks the app_registry) before obtaining the session lock.

* Updated res_stasis:__stasis_app_register to release the app_registry
  container lock before calling app_update (which locks the sesison).

ASTERISK-28423
Reported by Ross Beer

Change-Id: I58c69d08cb372852a63933608e4d6c3e456247b4
13.30
George Joseph 6 years ago
parent 3b94982b4f
commit 7a236ed20d

@ -124,6 +124,7 @@ static void app_handler(void *data, const char *app_name,
const char *msg_application = S_OR(
ast_json_string_get(ast_json_object_get(message, "application")),
"");
int app_debug_enabled;
if (!session) {
return;
@ -142,9 +143,12 @@ static void app_handler(void *data, const char *app_name,
return;
}
/* Don't lock the app_registry container with session locked */
app_debug_enabled = stasis_app_get_debug_by_name(app_name);
ao2_lock(session);
if (session->ws_session && stasis_app_event_allowed(app_name, message)) {
if (stasis_app_get_debug_by_name(app_name)) {
if (app_debug_enabled) {
char *str = ast_json_dump_string_format(message, ast_ari_json_format());
ast_verbose("<--- Sending ARI event to %s --->\n%s\n",

@ -1677,7 +1677,7 @@ struct ao2_container *stasis_app_get_all(void)
static int __stasis_app_register(const char *app_name, stasis_app_cb handler, void *data, int all_events)
{
struct stasis_app *app;
RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
if (!apps_registry) {
return -1;
@ -1686,36 +1686,43 @@ static int __stasis_app_register(const char *app_name, stasis_app_cb handler, vo
ao2_lock(apps_registry);
app = ao2_find(apps_registry, app_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (app) {
/*
* We need to unlock the apps_registry before calling app_update to
* prevent the possibility of a deadlock with the session. We'll still
* run the lazy cleanup first though.
*/
cleanup();
ao2_unlock(apps_registry);
app_update(app, handler, data);
} else {
app = app_create(app_name, handler, data, all_events ? STASIS_APP_SUBSCRIBE_ALL : STASIS_APP_SUBSCRIBE_MANUAL);
if (!app) {
ao2_unlock(apps_registry);
return -1;
}
return 0;
}
if (all_events) {
struct stasis_app_event_source *source;
app = app_create(app_name, handler, data, all_events ? STASIS_APP_SUBSCRIBE_ALL : STASIS_APP_SUBSCRIBE_MANUAL);
if (!app) {
ao2_unlock(apps_registry);
return -1;
}
AST_RWLIST_RDLOCK(&event_sources);
AST_LIST_TRAVERSE(&event_sources, source, next) {
if (!source->subscribe) {
continue;
}
if (all_events) {
struct stasis_app_event_source *source;
source->subscribe(app, NULL);
AST_RWLIST_RDLOCK(&event_sources);
AST_LIST_TRAVERSE(&event_sources, source, next) {
if (!source->subscribe) {
continue;
}
AST_RWLIST_UNLOCK(&event_sources);
source->subscribe(app, NULL);
}
ao2_link_flags(apps_registry, app, OBJ_NOLOCK);
AST_RWLIST_UNLOCK(&event_sources);
}
ao2_link_flags(apps_registry, app, OBJ_NOLOCK);
/* We lazily clean up the apps_registry, because it's good enough to
* prevent memory leaks, and we're lazy.
*/
cleanup();
ao2_unlock(apps_registry);
ao2_ref(app, -1);
return 0;
}

Loading…
Cancel
Save