]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
func_odbc: Fix connection deadlock. 72/3172/1
authorJoshua Colp <jcolp@digium.com>
Mon, 11 Jul 2016 00:08:28 +0000 (21:08 -0300)
committerJoshua Colp <jcolp@digium.com>
Mon, 11 Jul 2016 00:42:02 +0000 (21:42 -0300)
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
include/asterisk/res_odbc.h
res/res_odbc.c

index b067f968aaaf6be69a9dad76947d96fc723c74db..e2ca7c823d0b302efc504a67e3df3b90411dbd4a 100644 (file)
@@ -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;
index 8c7b549506357ab81e9cf689ac08c11782a10468..137f7d4a5458d82c208cae52c2c32de512b5b1b1 100644 (file)
@@ -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 */
index b2204ff09972ebace1c33ebd3302322b06bcccaa..31ea29b03532f9585ac26ec001d0219ed952a741 100644 (file)
@@ -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.
  *