diff --git a/res/res_odbc.c b/res/res_odbc.c index 9103dbc3ed..5e0ea4b548 100644 --- a/res/res_odbc.c +++ b/res/res_odbc.c @@ -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; }