summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2016-07-10 21:08:28 -0300
committerJoshua Colp <jcolp@digium.com>2016-07-12 05:00:16 -0500
commit4ad333bb0e365b8a2795756225794c9d0b09e05c (patch)
treebf6ee35422453a71d469bc803271331aef711b64
parent44f16af7cc9a6bc5203106a81b595997cb31c4fd (diff)
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
-rw-r--r--funcs/func_odbc.c16
-rw-r--r--include/asterisk/res_odbc.h5
-rw-r--r--res/res_odbc.c16
3 files changed, 37 insertions, 0 deletions
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.
*