From 4ad333bb0e365b8a2795756225794c9d0b09e05c Mon Sep 17 00:00:00 2001 From: Joshua Colp Date: Sun, 10 Jul 2016 21:08:28 -0300 Subject: func_odbc: Fix connection deadlock. The func_odbc module was modified to ensure that the previous behavior of using a single database connection was maintained. This was done by getting a single database connection and holding on to it. With the new multiple connection support in res_odbc this will actually starve every other thread from getting access to the database as it also maintains the previous behavior of having only a single database connection. This change disables the func_odbc specific behavior if the res_odbc module is running with only a single database connection active. The connection is only kept for the duration of the request. ASTERISK-26177 #close Change-Id: I9bdbd8a300fb3233877735ad3fd07bce38115b7f --- funcs/func_odbc.c | 16 ++++++++++++++++ include/asterisk/res_odbc.h | 5 +++++ res/res_odbc.c | 16 ++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c index 489f3734b..224cd7a7f 100644 --- a/funcs/func_odbc.c +++ b/funcs/func_odbc.c @@ -388,9 +388,25 @@ static struct odbc_obj *get_odbc_obj(const char *dsn_name, struct dsn **dsn) static inline void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn) { if (dsn && *dsn) { + /* If multiple connections are not enabled then the guarantee + * of a single connection already exists and holding on to the + * connection would prevent any other user from acquiring it + * indefinitely. + */ + if (ast_odbc_get_max_connections((*dsn)->name) < 2) { + ast_odbc_release_obj((*dsn)->connection); + (*dsn)->connection = NULL; + } ao2_unlock(*dsn); ao2_ref(*dsn, -1); *dsn = NULL; + /* Some callers may provide both an obj and dsn. To ensure that + * the connection is not released twice we set it to NULL here if + * present. + */ + if (obj) { + *obj = NULL; + } } else if (obj && *obj) { ast_odbc_release_obj(*obj); *obj = NULL; diff --git a/include/asterisk/res_odbc.h b/include/asterisk/res_odbc.h index 8c7b54950..137f7d4a5 100644 --- a/include/asterisk/res_odbc.h +++ b/include/asterisk/res_odbc.h @@ -243,4 +243,9 @@ int ast_odbc_text2isolation(const char *txt); */ const char *ast_odbc_isolation2text(int iso); +/*! + * \brief Return the current configured maximum number of connections for a class + */ +unsigned int ast_odbc_get_max_connections(const char *name); + #endif /* _ASTERISK_RES_ODBC_H */ diff --git a/res/res_odbc.c b/res/res_odbc.c index a89c95492..bd64b9fef 100644 --- a/res/res_odbc.c +++ b/res/res_odbc.c @@ -744,6 +744,22 @@ static int aoro2_class_cb(void *obj, void *arg, int flags) return 0; } +unsigned int ast_odbc_get_max_connections(const char *name) +{ + struct odbc_class *class; + unsigned int max_connections; + + class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name); + if (!class) { + return 0; + } + + max_connections = class->maxconnections; + ao2_ref(class, -1); + + return max_connections; +} + /* * \brief Determine if the connection has died. * -- cgit v1.2.3