connpool.c 3.86 KB
Newer Older
1 2 3 4 5
#include "common.h"
#include "list.h"
#include "connection.h"
#include "connpool.h"

6 7
#include <stdio.h>

8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

/**
 * Initializes the given connection pool. New connections created by the
 * pool will connect to the given endpoint.
 */
void
connection_pool_init (struct connection_pool * cp, struct addrinfo * endpoint) {
	assert (cp != NULL);
	assert (endpoint != NULL);

	cp->connections_count = 0;
	list_init (&cp->free_connections);
	list_init (&cp->busy_connections);

	cp->endpoint = endpoint;
	cp->after_open_hook = NULL;
	cp->before_close_hook = NULL;
}


/**
 * Sets a callback to be invoked after a new connection has been opened.
 * The connection pool may open new connections when there is no unused
 * connection available.
 */
void
connection_pool_set_after_open_hook (struct connection_pool * cp, connection_hook_fn after_open_fn) {
	assert (cp != NULL);

	cp->after_open_hook = after_open_fn;
}


/**
 * Sets a callback to be invoked before closing a connection.
 * The connection pool may close connections when there are too many
 * connections open, or when the connection pool itself is being destroyed.
 */
void
connection_pool_set_before_close_hook (struct connection_pool * cp, connection_hook_fn before_close_fn) {
	assert (cp != NULL);

	cp->before_close_hook = before_close_fn;
}


/**
 * Acquires a connection from a pool of available connections. Creates a new
 * connection if none is currently available. This operation is thread-unsafe.
 */
struct connection *
connection_pool_get_connection (struct connection_pool * cp) {
	assert (cp != NULL);

	dlprintf ("getting connection for %s: ", cp->endpoint->ai_canonname);

	//
	// Grab the first available connection and return. If there is no connection
	// available, create a new one and add it to the busy connection list.
	//
	if (!list_is_empty (&cp->free_connections)) {
		dprintf ("reusing a connection [%d in total]\n", cp->connections_count);
		struct list * item = list_remove_after (&cp->free_connections);
		list_insert_after (item, &cp->busy_connections);
		return list_item (item, struct connection, cp_link);

	} else {
		struct connection * connection = connection_open (cp->endpoint);
		if (cp->after_open_hook != NULL) {
			cp->after_open_hook (connection);
		}

		list_insert_after (&connection->cp_link, &cp->busy_connections);
		cp->connections_count++;

		dprintf ("created new connection [%d in total]\n", cp->connections_count);
		return connection;
	}
}


/**
 * Releases the given connection and puts it back to the pool of available
 * connections. This operation is thread-unsafe.
 */
void
connection_pool_put_connection (
	struct connection_pool * cp, struct connection * connection
) {
	assert (cp != NULL);
	assert (connection != NULL);

	//
	// Move the connection from the list of busy connections to
	// the list of available connections.
	//
	struct list * item = list_remove (&connection->cp_link);
	list_insert_after (item, &cp->free_connections);
}

//

static void
__connection_destructor (struct list * item, void * data) {
	struct connection * connection = list_item (item, struct connection, cp_link);

	struct connection_pool * cp = (struct connection_pool *) data;
	if (cp->before_close_hook != NULL) {
		cp->before_close_hook (connection);
	}

	connection_close (connection);
	cp->connections_count--;
}


/**
 * Closes all connections in the given connection pool. The connection pool
 * may be reused to open new connections to the host it was initialized for.
 */
void
connection_pool_close (struct connection_pool * cp) {
	assert (cp != NULL);

	dlprintf (
		"closing pool for %s: max connections %d\n",
		cp->endpoint->ai_canonname, cp->connections_count
	);

	list_destroy (&cp->free_connections, __connection_destructor, (void *) cp);
	if (!list_is_empty (&cp->busy_connections)) {
139
		warn ("closing %d active connections", cp->connections_count);
140 141 142
		list_destroy (&cp->busy_connections, __connection_destructor, (void *) cp);
	}
}