memcache/server.c
author Tero Marttila <terom@fixme.fi>
Thu, 28 Aug 2008 03:12:11 +0300
changeset 46 8a832c0e01ee
parent 43 e5b714190dee
child 48 1c67f512779b
permissions -rw-r--r--
bugfixed, enum->string mappings, test does requests

#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);
}