fr_dlist_talloc_free(&query->referrals);
- /*
- * If the connection this query was using has no pending queries and
- * is no-longer associated with a fr_connection_t then free it
- */
- if ((query->ldap_conn) && (query->ldap_conn->conn == NULL) &&
- (fr_rb_num_elements(query->ldap_conn->queries) == 0)) {
- talloc_free(query->ldap_conn);
+ if (query->ldap_conn) {
+ /*
+ * Remove the query from the list of references to its connection
+ */
+ fr_dlist_remove(&query->ldap_conn->refs, query);
+
+ /*
+ * If the connection this query was using has no pending queries and
+ * is no-longer associated with a fr_connection_t then free it
+ */
+ if (!query->ldap_conn->conn && (fr_dlist_num_elements(&query->ldap_conn->refs) == 0) &&
+ (fr_rb_num_elements(query->ldap_conn->queries) == 0)) talloc_free(query->ldap_conn);
}
/*
int fd; //!< File descriptor for this connection.
fr_rb_tree_t *queries; //!< Outstanding queries on this connection
+ fr_dlist_head_t refs; //!< Replied to queries still referencing this connection.
void *uctx; //!< User data associated with the handle.
} fr_ldap_connection_t;
*/
struct fr_ldap_query_s {
fr_rb_node_t node; //!< Entry in the tree of outstanding queries.
+ fr_dlist_t entry; //!< Entry in the list of connection references.
LDAPURLDesc *ldap_url; //!< parsed URL for current query if the source
///< of the query was a URL.
/*
* If there are any pending queries, don't free
*/
- if ((c->queries) && (fr_rb_num_elements(c->queries) > 0)) return -1;
+ if (((c->queries) && (fr_rb_num_elements(c->queries) > 0)) || (fr_dlist_num_elements(&c->refs) > 0)) return -1;
talloc_free_children(c); /* Force inverted free order */
{
fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(uctx, fr_ldap_connection_t);
- if (fr_rb_num_elements(ldap_conn->queries) == 0) return;
+ if ((fr_rb_num_elements(ldap_conn->queries) == 0) && (fr_dlist_num_elements(&ldap_conn->refs) == 0)) return;
talloc_reparent(conn, NULL, ldap_conn);
ldap_conn->conn = NULL;
* Initialise tree for outstanding queries handled by this connection
*/
MEM(c->queries = fr_rb_inline_talloc_alloc(c, fr_ldap_query_t, node, fr_ldap_query_cmp, NULL));
+ fr_dlist_init(&c->refs, fr_ldap_query_t, entry);
*h = c; /* Set the handle */
if (status != LDAP_PROC_SUCCESS) goto error;
+ /*
+ * If the query has previously been associated with a different
+ * connection, remove that reference. Typically when following references.
+ */
+ if (query->ldap_conn) fr_dlist_remove(&query->ldap_conn->refs, query);
+
/*
* Record which connection was used for this query
* - results processing often needs access to an LDAP handle
*/
fr_rb_remove(ldap_conn->queries, query);
+ /*
+ * Add the query to the list of queries referencing this connection.
+ * Prevents the connection from being freed until the query has finished using it.
+ */
+ fr_dlist_insert_tail(&ldap_conn->refs, query);
+
/*
* This really shouldn't happen - as we only retrieve complete sets of results -
* but as the query data structure will last until its results are fully handled