#include <stdlib.h>
#include <assert.h>
#include "server.h"
#include "connection.h"
#include "request.h"
#include "../memcache.h"
#include "../common.h"
struct memcache_server *memcache_server_alloc (struct config_endpoint *endpoint, int max_connections) {
struct memcache_server *server = NULL;
if ((server = calloc(1, sizeof(*server))) == NULL)
ERROR("calloc");
// store the vars
server->endpoint = endpoint;
server->max_connections = max_connections;
// init lists
LIST_INIT(&server->conn_list);
TAILQ_INIT(&server->req_queue);
// grow connpool so as to have a connection ready
if (memcache_server_grow_connpool(server))
ERROR("error opening initial connection");
// success
return server;
error:
free(server);
return NULL;
}
int memcache_server_grow_connpool (struct memcache_server *server) {
struct memcache_conn *conn;
int count;
// count connections
for (count = 0, conn = server->conn_list.lh_first; conn != NULL; conn = conn->connlist_node.le_next, count++) ;
// room for more?
if (count < server->max_connections) {
// create a new one
if ((conn = memcache_conn_open(server)) == NULL)
return -1;
// enlist it
LIST_INSERT_HEAD(&server->conn_list, conn, connlist_node);
// the connection will call memcache_server_coon_ready once it's ready for use...
}
// success
return 0;
}
int memcache_server_add_req (struct memcache_server *server, struct memcache_req *req) {
struct memcache_conn *conn;
// look for an idle connection
for (conn = server->conn_list.lh_first; conn != NULL; conn = conn->connlist_node.le_next) {
if (memcache_conn_is_available(conn)) {
// we found an idle connection
break;
}
}
if (conn != NULL) {
// we found an available connection
// if the request fails, then we will know via conn_dead
memcache_conn_do_req(conn, req);
return 0;
} else {
// enqueue the request until a connection is available
// XXX: queue size limits
TAILQ_INSERT_TAIL(&server->req_queue, req, reqqueue_node);
// notify the req
memcache_req_queued(req);
return 0;
}
}
void memcache_server_conn_ready (struct memcache_server *server, struct memcache_conn *conn) {
assert(server == conn->server);
// do we have any queued requests waiting?
struct memcache_req *req = server->req_queue.tqh_first;
if (req) {
// remove it from the queue and execute it
TAILQ_REMOVE(&server->req_queue, req, reqqueue_node);
// this will take care of any error handling by itself
memcache_conn_do_req(conn, req);
}
}
void memcache_server_conn_dead (struct memcache_server *server, struct memcache_conn *conn) {
assert(server == conn->server);
// XXX: reconnect/error out requests?
// remove it from the list
LIST_REMOVE(conn, connlist_node);
// free it
memcache_conn_free(conn);
}