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);
371 if ((fc->lifetime > 0) && ((this->start + fc->lifetime) < now)){
372 DEBUG("%s: Closing expired connection (%i) ",
373 fc->log_prefix, this->number);
377 if ((fc->idle_timeout > 0) && ((this->last_used + fc->idle_timeout) < now)){
378 DEBUG("%s: Closing idle connection (%i)",
379 fc->log_prefix, this->number);
387 static int fr_connection_pool_check(fr_connection_pool_t *fc)
390 time_t now = time(NULL);
391 fr_connection_t *this;
393 if (fc->last_checked == now) return 1;
395 pthread_mutex_lock(&fc->mutex);
397 spare = fc->num - fc->active;
400 if ((fc->num < fc->max) && (spare < fc->spare)) {
401 spawn = fc->spare - spare;
402 if ((spawn + fc->num) > fc->max) {
403 spawn = fc->max - fc->num;
405 if (fc->spawning) spawn = 0;
408 pthread_mutex_unlock(&fc->mutex);
409 fr_connection_spawn(fc, now); /* ignore return code */
414 * We haven't spawned connections in a while, and there
415 * are too many spare ones. Close the one which has been
416 * idle for the longest.
418 if ((now >= (fc->last_spawned + fc->cleanup_delay)) &&
419 (spare > fc->spare)) {
420 fr_connection_t *idle;
423 for (this = fc->tail; this != NULL; this = this->prev) {
424 if (this->used) continue;
427 (this->last_used < idle->last_used)) {
432 rad_assert(idle != NULL);
434 DEBUG("%s: Closing idle connection (%i): Too many free connections",
435 fc->log_prefix, idle->number);
436 fr_connection_close(fc, idle);
440 * Pass over all of the connections in the pool, limiting
441 * lifetime, idle time, max requests, etc.
443 for (this = fc->head; this != NULL; this = this->next) {
444 fr_connection_manage(fc, this, now);
447 fc->last_checked = now;
448 pthread_mutex_unlock(&fc->mutex);
453 int fr_connection_check(fr_connection_pool_t *fc, void *conn)
456 fr_connection_t *this;
461 if (!conn) return fr_connection_pool_check(fc);
464 pthread_mutex_lock(&fc->mutex);
466 for (this = fc->head; this != NULL; this = this->next) {
467 if (this->connection == conn) {
468 rcode = fr_connection_manage(fc, conn, now);
473 pthread_mutex_unlock(&fc->mutex);
479 void *fr_connection_get(fr_connection_pool_t *fc)
482 fr_connection_t *this, *next;
484 if (!fc) return NULL;
486 pthread_mutex_lock(&fc->mutex);
489 for (this = fc->head; this != NULL; this = next) {
492 if (!fr_connection_manage(fc, this, now)) continue;
494 if (!this->used) goto do_return;
497 if (fc->num == fc->max) {
499 * Rate-limit complaints.
501 if (fc->last_complained != now) {
502 radlog(L_ERR, "%s: No connections available and at max connection limit",
504 fc->last_complained = now;
506 pthread_mutex_unlock(&fc->mutex);
510 pthread_mutex_unlock(&fc->mutex);
511 this = fr_connection_spawn(fc, now);
512 if (!this) return NULL;
513 pthread_mutex_lock(&fc->mutex);
518 this->last_used = now;
521 pthread_mutex_unlock(&fc->mutex);
523 DEBUG("%s: Reserved connection (%i)", fc->log_prefix, this->number);
525 return this->connection;
528 void fr_connection_release(fr_connection_pool_t *fc, void *conn)
530 fr_connection_t *this;
532 if (!fc || !conn) return;
534 pthread_mutex_lock(&fc->mutex);
537 * FIXME: This loop could be avoided if we passed a 'void
538 * **connection' instead. We could use "offsetof" in
539 * order to find top of the parent structure.
541 for (this = fc->head; this != NULL; this = this->next) {
542 if (this->connection == conn) {
543 rad_assert(this->used == TRUE);
547 * Put it at the head of the list, so
548 * that it will get re-used quickly.
550 if (this != fc->head) {
551 fr_connection_unlink(fc, this);
552 fr_connection_link(fc, this);
554 rad_assert(fc->active > 0);
560 pthread_mutex_unlock(&fc->mutex);
562 DEBUG("%s: Released connection (%i)", fc->log_prefix, this->number);
565 * We mirror the "spawn on get" functionality by having
566 * "delete on release". If there are too many spare
567 * connections, go manage the pool && clean some up.
569 fr_connection_pool_check(fc);
573 void *fr_connection_reconnect(fr_connection_pool_t *fc, void *conn)
576 fr_connection_t *this;
579 if (!fc || !conn) return NULL;
581 pthread_mutex_lock(&fc->mutex);
583 conn_number = this->number;
586 * FIXME: This loop could be avoided if we passed a 'void
587 * **connection' instead. We could use "offsetof" in
588 * order to find top of the parent structure.
590 for (this = fc->head; this != NULL; this = this->next) {
591 if (this->connection != conn) continue;
593 rad_assert(this->used == TRUE);
595 DEBUG("%s: Reconnecting (%i)", fc->log_prefix, conn_number);
597 new_conn = fc->create(fc->ctx);
599 time_t now = time(NULL);
601 if (fc->last_complained == now) {
604 fc->last_complained = now;
607 fr_connection_close(fc, conn);
608 pthread_mutex_unlock(&fc->mutex);
611 * Can't create a new socket.
612 * Try grabbing a pre-existing one.
614 new_conn = fr_connection_get(fc);
615 if (new_conn) return new_conn;
617 if (!now) return NULL;
619 radlog(L_ERR, "%s: Failed to reconnect (%i), and no other connections available",
620 fc->log_prefix, conn_number);
624 fc->delete(fc->ctx, conn);
625 this->connection = new_conn;
626 pthread_mutex_unlock(&fc->mutex);
630 pthread_mutex_unlock(&fc->mutex);
633 * Caller passed us something that isn't in the pool.