//!< cleanup. Initialized to
//!< cleanup_interval, and decays
//!< from there.
- uint32_t max_uses; //!< Maximum number of times a
+ uint64_t max_uses; //!< Maximum number of times a
//!< connection can be used before being
//!< closed.
uint32_t lifetime; //!< How long a connection can be open
CONF_SECTION *cs; //!< Configuration section holding
//!< the section of parsed config file
//!< that relates to this pool.
- void *ctx; //!< Pointer to context data that will
+ void *opaque; //!< Pointer to context data that will
//!< be passed to callbacks.
char *log_prefix; //!< Log prefix to prepend to all log
//!< connections.
fr_connection_alive_t alive; //!< Function used to check status
//!< of connections.
- fr_connection_delete_t delete; //!< Function used to close existing
- //!< connections.
};
#define LOG_PREFIX "rlm_%s (%s)"
#endif
static const CONF_PARSER connection_config[] = {
- { "start", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, start), "5" },
- { "min", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, min), "5" },
- { "max", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, max), "10" },
- { "spare", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, spare), "3" },
- { "uses", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, max_uses), "0" },
+ { "start", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, start), "5" },
+ { "min", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, min), "5" },
+ { "max", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, max), "10" },
+ { "spare", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, spare), "3" },
+ { "uses", FR_CONF_OFFSET(PW_TYPE_INTEGER64, fr_connection_pool_t, max_uses), "0" },
{ "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, lifetime), "0" },
{ "cleanup_delay", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, cleanup_interval), NULL},
{ "cleanup_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, cleanup_interval), "30" },
- { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, idle_timeout), "60" },
- { "retry_delay", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, retry_delay), "1" },
+ { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, idle_timeout), "60" },
+ { "retry_delay", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, retry_delay), "1" },
{ "spread", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_connection_pool_t, spread), "no" },
{ NULL, -1, 0, NULL, NULL }
};
static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
time_t now, bool in_use)
{
+ TALLOC_CTX *ctx;
+
fr_connection_t *this;
void *conn;
INFO("%s: Opening additional connection (%" PRIu64 ")", pool->log_prefix, pool->count);
/*
+ * Allocate a new top level ctx for the create callback
+ * to hang its memory off of.
+ */
+ ctx = talloc_init("fr_connection_ctx");
+ if (!ctx) return NULL;
+
+ /*
* This may take a long time, which prevents other
* threads from releasing connections. We don't care
* about other threads opening new connections, as we
* already have no free connections.
*/
- conn = pool->create(pool->ctx);
+ conn = pool->create(ctx, pool->opaque);
if (!conn) {
ERROR("%s: Opening connection failed (%" PRIu64 ")", pool->log_prefix, pool->count);
pthread_mutex_unlock(&pool->mutex);
return NULL;
}
+ fr_link_talloc_ctx_free(this, ctx);
this->created = now;
this->connection = conn;
if (pool->trigger) exec_trigger(NULL, pool->cs, "close", true);
fr_connection_unlink(pool, this);
- pool->delete(pool->ctx, this->connection);
rad_assert(pool->num > 0);
pool->num--;
talloc_free(this);
* @note Will call the 'start' trigger.
*
* @param[in] parent configuration section containing a 'pool' subsection.
- * @param[in] ctx pointer to pass to callbacks.
+ * @param[in] opaque data pointer to pass to callbacks.
* @param[in] c Callback to create new connections.
* @param[in] a Callback to check the status of connections.
- * @param[in] d Callback to delete connections.
* @param[in] prefix to prepend to all log message, if NULL will create prefix
* from parent conf section names.
* @return A new connection pool or NULL on error.
*/
fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
- void *ctx,
+ void *opaque,
fr_connection_create_t c,
fr_connection_alive_t a,
- fr_connection_delete_t d,
char const *prefix)
{
uint32_t i;
char const *cs_name1, *cs_name2;
time_t now = time(NULL);
- if (!parent || !ctx || !c || !d) return NULL;
+ if (!parent || !opaque || !c) return NULL;
cs = cf_section_sub_find(parent, "pool");
if (!cs) cs = cf_section_sub_find(parent, "limit");
- if (cs) {
- pool = talloc_zero(cs, fr_connection_pool_t);
- } else {
- pool = talloc_zero(parent, fr_connection_pool_t);
- }
+ /*
+ * Pool is allocated in the NULL context as
+ * threads are likely to allocate memory
+ * beneath the pool.
+ */
+ pool = talloc_zero(NULL, fr_connection_pool_t);
if (!pool) return NULL;
+ /*
+ * Ensure the pool is freed at the same time
+ * as its parent.
+ */
+ if (fr_link_talloc_ctx_free(cs ? cs : parent, pool) < 0) {
+ talloc_free(pool);
+ return NULL;
+ }
+
pool->cs = cs;
- pool->ctx = ctx;
+ pool->opaque = opaque;
pool->create = c;
pool->alive = a;
- pool->delete = d;
pool->head = pool->tail = NULL;
void *new_conn;
fr_connection_t *this;
uint64_t conn_number;
+ TALLOC_CTX *ctx;
if (!pool || !conn) return NULL;
+ /*
+ * If fr_connection_find is successful the pool is now locked
+ */
this = fr_connection_find(pool, conn);
if (!this) return NULL;
+
+ conn_number = this->number;
+
/*
- * The pool is now locked.
+ * Destroy any handles associated with the fr_connection_t
*/
- conn_number = this->number;
+ talloc_free_children(this);
DEBUG("%s: Reconnecting (%" PRIu64 ")", pool->log_prefix, conn_number);
- new_conn = pool->create(pool->ctx);
+ /*
+ * Allocate a new top level ctx for the create callback
+ * to hang its memory off of.
+ */
+ ctx = talloc_init("fr_connection_ctx");
+ if (!ctx) return NULL;
+ fr_link_talloc_ctx_free(this, ctx);
+
+ new_conn = pool->create(ctx, pool->opaque);
if (!new_conn) {
/*
* We can't create a new connection, so close
new_conn = fr_connection_get_internal(pool, false);
if (new_conn) return new_conn;
- RATE_LIMIT(ERROR("%s: Failed to reconnect (%" PRIu64 "), no free connections are available", pool->log_prefix,
- conn_number));
+ RATE_LIMIT(ERROR("%s: Failed to reconnect (%" PRIu64 "), no free connections are available",
+ pool->log_prefix, conn_number));
+
return NULL;
}
if (pool->trigger) exec_trigger(NULL, pool->cs, "close", true);
- pool->delete(pool->ctx, conn);
this->connection = new_conn;
pthread_mutex_unlock(&pool->mutex);
+
return new_conn;
}