3 * @brief Handle pools of connections (threads, sockets, etc.)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2011 The FreeRADIUS server project
22 * Copyright 2011 Alan DeKok <aland@deployingradius.com>
25 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/connection.h>
32 #include <freeradius-devel/rad_assert.h>
34 typedef struct fr_connection_t fr_connection_t;
36 struct fr_connection_t {
37 fr_connection_t *prev, *next;
44 int number; /* unique ID */
48 struct fr_connection_pool_t {
55 unsigned int count; /* num connections spawned */
56 int num; /* num connections in pool */
57 int active; /* num connections active */
62 time_t last_complained;
70 fr_connection_t *head, *tail;
73 pthread_mutex_t mutex;
81 fr_connection_create_t create;
82 fr_connection_alive_t alive;
83 fr_connection_delete_t delete;
86 #define LOG_PREFIX "rlm_%s (%s)"
87 #ifndef HAVE_PTHREAD_H
88 #define pthread_mutex_lock(_x)
89 #define pthread_mutex_unlock(_x)
92 static const CONF_PARSER connection_config[] = {
93 { "start", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, start),
95 { "min", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, min),
97 { "max", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, max),
99 { "spare", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, spare),
101 { "uses", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, max_uses),
103 { "lifetime", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, lifetime),
105 { "cleanup_delay", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, cleanup_delay),
107 { "idle_timeout", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, idle_timeout),
109 { "lazy", PW_TYPE_BOOLEAN, offsetof(fr_connection_pool_t, lazy_init),
111 { NULL, -1, 0, NULL, NULL }
114 static void fr_connection_unlink(fr_connection_pool_t *fc,
115 fr_connection_t *this)
119 rad_assert(fc->head != this);
120 this->prev->next = this->next;
122 rad_assert(fc->head == this);
123 fc->head = this->next;
126 rad_assert(fc->tail != this);
127 this->next->prev = this->prev;
129 rad_assert(fc->tail == this);
130 fc->tail = this->prev;
133 this->prev = this->next = NULL;
137 static void fr_connection_link(fr_connection_pool_t *fc,
138 fr_connection_t *this)
140 rad_assert(fc != NULL);
141 rad_assert(this != NULL);
142 rad_assert(fc->head != this);
143 rad_assert(fc->tail != this);
145 if (fc->head) fc->head->prev = this;
146 this->next = fc->head;
150 rad_assert(this->next == NULL);
153 rad_assert(this->next != NULL);
159 * Called with the mutex free.
161 static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *fc,
164 fr_connection_t *this;
167 rad_assert(fc != NULL);
169 pthread_mutex_lock(&fc->mutex);
170 rad_assert(fc->num <= fc->max);
172 if ((fc->last_failed == now) || fc->spawning) {
173 pthread_mutex_unlock(&fc->mutex);
180 * Unlock the mutex while we try to open a new
181 * connection. If there are issues with the back-end,
182 * opening a new connection may take a LONG time. In
183 * that case, we want the other connections to continue
186 pthread_mutex_unlock(&fc->mutex);
188 DEBUG("%s: Opening additional connection (%i)",
189 fc->log_prefix, fc->count);
191 this = rad_malloc(sizeof(*this));
192 memset(this, 0, sizeof(*this));
195 * This may take a long time, which prevents other
196 * threads from releasing connections. We don't care
197 * about other threads opening new connections, as we
198 * already have no free connections.
200 conn = fc->create(fc->ctx);
202 fc->last_failed = now;
204 fc->spawning = FALSE; /* atomic, so no lock is needed */
209 this->connection = conn;
212 * And lock the mutex again while we link the new
213 * connection back into the pool.
215 pthread_mutex_lock(&fc->mutex);
217 this->number = fc->count++;
218 this->last_used = now;
219 fr_connection_link(fc, this);
221 fc->spawning = FALSE;
222 fc->last_spawned = time(NULL);
224 pthread_mutex_unlock(&fc->mutex);
229 static void fr_connection_close(fr_connection_pool_t *fc,
230 fr_connection_t *this)
232 rad_assert(this->used == FALSE);
234 fr_connection_unlink(fc, this);
235 fc->delete(fc->ctx, this->connection);
236 rad_assert(fc->num > 0);
243 void fr_connection_pool_delete(fr_connection_pool_t *fc)
245 fr_connection_t *this, *next;
247 DEBUG("%s: Removing connection pool", fc->log_prefix);
249 pthread_mutex_lock(&fc->mutex);
251 for (this = fc->head; this != NULL; this = next) {
253 DEBUG("%s: Closing connection (%i)", fc->log_prefix, this->number);
254 fr_connection_close(fc, this);
257 rad_assert(fc->head == NULL);
258 rad_assert(fc->tail == NULL);
259 rad_assert(fc->num == 0);
261 cf_section_parse_free(fc->cs, fc);
263 free(fc->log_prefix);
267 fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
269 fr_connection_create_t c,
270 fr_connection_alive_t a,
271 fr_connection_delete_t d)
274 fr_connection_pool_t *fc;
275 fr_connection_t *this;
277 const char *cs_name1, *cs_name2;
278 time_t now = time(NULL);
280 if (!parent || !ctx || !c || !d) return NULL;
282 cs = cf_section_sub_find(parent, "pool");
284 cf_log_err(cf_sectiontoitem(parent), "No \"pool\" subsection found");
288 fc = rad_malloc(sizeof(*fc));
289 memset(fc, 0, sizeof(*fc));
297 fc->head = fc->tail = NULL;
299 #ifdef HAVE_PTHREAD_H
300 pthread_mutex_init(&fc->mutex, NULL);
303 cs_name1 = cf_section_name1(parent);
304 cs_name2 = cf_section_name2(parent);
309 lp_len = (sizeof(LOG_PREFIX) - 4) + strlen(cs_name1) + strlen(cs_name2);
310 fc->log_prefix = rad_malloc(lp_len);
311 snprintf(fc->log_prefix, lp_len, LOG_PREFIX, cs_name1, cs_name2);
313 DEBUG("%s: Initialising connection pool", fc->log_prefix);
315 if (cf_section_parse(cs, fc, connection_config) < 0) {
322 if (fc->max > 1024) fc->max = 1024;
323 if (fc->start > fc->max) fc->start = fc->max;
324 if (fc->spare > (fc->max - fc->min)) {
325 fc->spare = fc->max - fc->min;
327 if ((fc->lifetime > 0) && (fc->idle_timeout > fc->lifetime)) {
328 fc->idle_timeout = 0;
332 * Create all of the connections, unless the admin says
335 if (!fc->lazy_init) for (i = 0; i < fc->start; i++) {
336 this = fr_connection_spawn(fc, now);
339 fr_connection_pool_delete(fc);
349 * Called with the mutex lock held.
351 static int fr_connection_manage(fr_connection_pool_t *fc,
352 fr_connection_t *this,
355 rad_assert(fc != NULL);
356 rad_assert(this != NULL);
359 * Don't terminated in-use connections
361 if (this->used) return 1;
363 if ((fc->max_uses > 0) && (this->num_uses >= fc->max_uses)) {
364 DEBUG("%s: Closing expired connection (%i): Hit max_uses limit",
365 fc->log_prefix, this->number);
367 fr_connection_close(fc, this);
368 pthread_mutex_unlock(&fc->mutex);
372 if ((fc->lifetime > 0) && ((this->start + fc->lifetime) < now)){
373 DEBUG("%s: Closing expired connection (%i) ",
374 fc->log_prefix, this->number);
378 if ((fc->idle_timeout > 0) && ((this->last_used + fc->idle_timeout) < now)){
379 DEBUG("%s: Closing idle connection (%i)",
380 fc->log_prefix, this->number);
388 static int fr_connection_pool_check(fr_connection_pool_t *fc)
391 time_t now = time(NULL);
392 fr_connection_t *this;
394 if (fc->last_checked == now) return 1;
396 pthread_mutex_lock(&fc->mutex);
398 spare = fc->num - fc->active;
401 if ((fc->num < fc->max) && (spare < fc->spare)) {
402 spawn = fc->spare - spare;
403 if ((spawn + fc->num) > fc->max) {
404 spawn = fc->max - fc->num;
406 if (fc->spawning) spawn = 0;
409 pthread_mutex_unlock(&fc->mutex);
410 fr_connection_spawn(fc, now); /* ignore return code */
415 * We haven't spawned connections in a while, and there
416 * are too many spare ones. Close the one which has been
417 * idle for the longest.
419 if ((now >= (fc->last_spawned + fc->cleanup_delay)) &&
420 (spare > fc->spare)) {
421 fr_connection_t *idle;
424 for (this = fc->tail; this != NULL; this = this->prev) {
425 if (this->used) continue;
428 (this->last_used < idle->last_used)) {
433 rad_assert(idle != NULL);
435 DEBUG("%s: Closing idle connection (%i): Too many free connections",
436 fc->log_prefix, idle->number);
437 fr_connection_close(fc, idle);
441 * Pass over all of the connections in the pool, limiting
442 * lifetime, idle time, max requests, etc.
444 for (this = fc->head; this != NULL; this = this->next) {
445 fr_connection_manage(fc, this, now);
448 fc->last_checked = now;
449 pthread_mutex_unlock(&fc->mutex);
454 int fr_connection_check(fr_connection_pool_t *fc, void *conn)
457 fr_connection_t *this;
462 if (!conn) return fr_connection_pool_check(fc);
465 pthread_mutex_lock(&fc->mutex);
467 for (this = fc->head; this != NULL; this = this->next) {
468 if (this->connection == conn) {
469 rcode = fr_connection_manage(fc, conn, now);
474 pthread_mutex_unlock(&fc->mutex);
480 void *fr_connection_get(fr_connection_pool_t *fc)
483 fr_connection_t *this, *next;
485 if (!fc) return NULL;
487 pthread_mutex_lock(&fc->mutex);
490 for (this = fc->head; this != NULL; this = next) {
493 if (!fr_connection_manage(fc, this, now)) continue;
495 if (!this->used) goto do_return;
498 if (fc->num == fc->max) {
500 * Rate-limit complaints.
502 if (fc->last_complained != now) {
503 radlog(L_ERR, "%s: No connections available and at max connection limit",
505 fc->last_complained = now;
507 pthread_mutex_unlock(&fc->mutex);
511 pthread_mutex_unlock(&fc->mutex);
512 this = fr_connection_spawn(fc, now);
513 if (!this) return NULL;
514 pthread_mutex_lock(&fc->mutex);
519 this->last_used = now;
522 pthread_mutex_unlock(&fc->mutex);
524 DEBUG("%s: Reserved connection (%i)", fc->log_prefix, this->number);
526 return this->connection;
529 void fr_connection_release(fr_connection_pool_t *fc, void *conn)
531 fr_connection_t *this;
533 if (!fc || !conn) return;
535 pthread_mutex_lock(&fc->mutex);
538 * FIXME: This loop could be avoided if we passed a 'void
539 * **connection' instead. We could use "offsetof" in
540 * order to find top of the parent structure.
542 for (this = fc->head; this != NULL; this = this->next) {
543 if (this->connection == conn) {
544 rad_assert(this->used == TRUE);
548 * Put it at the head of the list, so
549 * that it will get re-used quickly.
551 if (this != fc->head) {
552 fr_connection_unlink(fc, this);
553 fr_connection_link(fc, this);
555 rad_assert(fc->active > 0);
561 pthread_mutex_unlock(&fc->mutex);
563 DEBUG("%s: Released connection (%i)", fc->log_prefix, this->number);
566 * We mirror the "spawn on get" functionality by having
567 * "delete on release". If there are too many spare
568 * connections, go manage the pool && clean some up.
570 fr_connection_pool_check(fc);
574 void *fr_connection_reconnect(fr_connection_pool_t *fc, void *conn)
577 fr_connection_t *this;
580 if (!fc || !conn) return NULL;
582 pthread_mutex_lock(&fc->mutex);
584 conn_number = this->number;
587 * FIXME: This loop could be avoided if we passed a 'void
588 * **connection' instead. We could use "offsetof" in
589 * order to find top of the parent structure.
591 for (this = fc->head; this != NULL; this = this->next) {
592 if (this->connection != conn) continue;
594 rad_assert(this->used == TRUE);
596 DEBUG("%s: Reconnecting (%i)", fc->log_prefix, conn_number);
598 new_conn = fc->create(fc->ctx);
600 time_t now = time(NULL);
602 if (fc->last_complained == now) {
605 fc->last_complained = now;
608 fr_connection_close(fc, conn);
609 pthread_mutex_unlock(&fc->mutex);
612 * Can't create a new socket.
613 * Try grabbing a pre-existing one.
615 new_conn = fr_connection_get(fc);
616 if (new_conn) return new_conn;
618 if (!now) return NULL;
620 radlog(L_ERR, "%s: Failed to reconnect (%i), and no other connections available",
621 fc->log_prefix, conn_number);
625 fc->delete(fc->ctx, conn);
626 this->connection = new_conn;
627 pthread_mutex_unlock(&fc->mutex);
631 pthread_mutex_unlock(&fc->mutex);
634 * Caller passed us something that isn't in the pool.