fix doc tpyos, rename some enums, fix printf format len for non-zero terminated strings (hg status), pass args to memcache_cmd_format_header via memcache_req_*, handle zero-length STORE requests, memcache_req is_buf_ours + free, other function name typos (keymemcache_req_key), fix req state behaviour re *_DATA_* for STORE requests and FETCH/END, better memcache_server connpool events/management, modular memcache_test with a working benchmark. This is a long commit message.
#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
memcache_server_grow_connpool(server);
// success
return server;
error:
free(server);
return NULL;
}
void memcache_server_grow_connpool (struct memcache_server *server) {
struct memcache_conn *conn;
int count = 0;
// count connections
LIST_FOREACH(conn, &server->conn_list, connlist_node) {
count++;
}
// room for more?
if (count < server->max_connections) {
// create a new one
if ((conn = memcache_conn_open(server)) == NULL)
ERROR("failed to grow the connpool");
// 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...
}
// ok
return;
error:
// XXX: we might be deadlocked now... requests queued, but no connections!
if (LIST_EMPTY(&server->conn_list) && !TAILQ_EMPTY(&server->req_queue))
FATAL("deadlock; requests queued, but no connections");
// XXX: harmless... but need some retry logic
}
int memcache_server_add_req (struct memcache_server *server, struct memcache_req *req) {
struct memcache_conn *conn;
// look for an idle connection
LIST_FOREACH(conn, &server->conn_list, connlist_node) {
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);
// grow the connpool, as we apparently don't have enough connections
memcache_server_grow_connpool(server);
return 0;
}
}
/*
* We might have available connections, process any queued requests, or try and grow the connpool if non available
*/
void memache_server_dequeue (struct memcache_server *server) {
struct memcache_conn *conn;
struct memcache_req *req;
// if no requests are queued, nothing needs doing
if ((req = TAILQ_FIRST(&server->req_queue)) == NULL)
return;
// look for idle connections to service the request
LIST_FOREACH(conn, &server->conn_list, connlist_node) {
if (memcache_conn_is_available(conn)) {
// remove the req 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);
// if that was the last req, return, otherwise continue
if ((req = TAILQ_FIRST(&server->req_queue)) == NULL)
return;
}
}
// no idle connections remaining, try and grow if applicable
memcache_server_grow_connpool(server);
}
void memcache_server_conn_ready (struct memcache_server *server, struct memcache_conn *conn) {
assert(server == conn->server);
// grab the next queued request
memache_server_dequeue(server);
}
void memcache_server_conn_dead (struct memcache_server *server, struct memcache_conn *conn) {
assert(server == conn->server);
// remove it from the list
LIST_REMOVE(conn, connlist_node);
// free it
memcache_conn_free(conn);
// this should grow the connpool back again
memache_server_dequeue(server);
}