Revert part of previous fix, and heavily comment the logic for object
authorTilghman Lesher <tilghman@meg.abyt.es>
Tue, 20 May 2008 16:13:48 +0000 (16:13 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Tue, 20 May 2008 16:13:48 +0000 (16:13 +0000)
destruction, for future users.
(Closes issue #12677)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@117262 65c4cc65-6c06-0410-ace0-fbb531ad65f3

res/res_odbc.c

index 9103dbc..5e0ea4b 100644 (file)
@@ -483,8 +483,9 @@ struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
        struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
 
        while ((class = ao2_iterator_next(&aoi))) {
-               if (!strcmp(class->name, name))
+               if (!strcmp(class->name, name) && !class->delme) {
                        break;
+               }
                ao2_ref(class, -1);
        }
 
@@ -514,7 +515,6 @@ struct odbc_obj *ast_odbc_request_obj(const char *name, int check)
                        obj->parent = class;
                        if (odbc_obj_connect(obj) == ODBC_FAIL) {
                                ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
-                               ast_mutex_destroy(&obj->lock);
                                ao2_ref(obj, -1);
                                obj = NULL;
                                class->count--;
@@ -630,27 +630,10 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj)
        return ODBC_SUCCESS;
 }
 
-static int class_is_delme(void *classobj, void *arg, int flags)
-{
-       struct odbc_class *class = classobj;
-
-       if (class->delme) {
-               struct odbc_obj *obj;
-               struct ao2_iterator aoi = ao2_iterator_init(class->obj_container, OBJ_UNLINK);
-               while ((obj = ao2_iterator_next(&aoi))) {
-                       ao2_ref(obj, -2);
-               }
-               return CMP_MATCH;
-       }
-
-       return 0;
-}
-
-
-
 static int reload(void)
 {
        struct odbc_class *class;
+       struct odbc_obj *current;
        struct ao2_iterator aoi = ao2_iterator_init(class_container, 0);
 
        /* First, mark all to be purged */
@@ -662,7 +645,50 @@ static int reload(void)
        load_odbc_config();
 
        /* Purge remaining classes */
-       ao2_callback(class_container, OBJ_NODATA | OBJ_UNLINK, class_is_delme, 0);
+
+       /* Note on how this works; this is a case of circular references, so we
+        * explicitly do NOT want to use a callback here (or we wind up in
+        * recursive hell).
+        *
+        * 1. Iterate through all the classes.  Note that the classes will currently
+        * contain two classes of the same name, one of which is marked delme and
+        * will be purged when all remaining objects of the class are released, and
+        * the other, which was created above when we re-parsed the config file.
+        * 2. On each class, there is a reference held by the master container and
+        * a reference held by each connection object.  There are two cases for
+        * destruction of the class, noted below.  However, in all cases, all O-refs
+        * (references to objects) will first be freed, which will cause the C-refs
+        * (references to classes) to be decremented (but never to 0, because the
+        * class container still has a reference).
+        *    a) If the class has outstanding objects, the C-ref by the class
+        *    container will then be freed, which leaves only C-refs by any
+        *    outstanding objects.  When the final outstanding object is released
+        *    (O-refs held by applications and dialplan functions), it will in turn
+        *    free the final C-ref, causing class destruction.
+        *    b) If the class has no outstanding objects, when the class container
+        *    removes the final C-ref, the class will be destroyed.
+        */
+       aoi = ao2_iterator_init(class_container, 0);
+       while ((class = ao2_iterator_next(&aoi))) { /* C-ref++ (by iterator) */
+               if (class->delme) {
+                       struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
+                       while ((current = ao2_iterator_next(&aoi2))) { /* O-ref++ (by iterator) */
+                               ao2_unlink(class->obj_container, current); /* unlink O-ref from class (reference handled implicitly) */
+                               ao2_ref(current, -1); /* O-ref-- (by iterator) */
+                               /* At this point, either
+                                * a) there's an outstanding O-ref, or
+                                * b) the object has already been destroyed.
+                                */
+                       }
+                       ao2_unlink(class_container, class); /* unlink C-ref from container (reference handled implicitly) */
+                       /* At this point, either
+                        * a) there's an outstanding O-ref, which holds an outstanding C-ref, or
+                        * b) the last remaining C-ref is held by the iterator, which will be
+                        * destroyed in the next step.
+                        */
+               }
+               ao2_ref(class, -1); /* C-ref-- (by iterator) */
+       }
 
        return 0;
 }