cache/req.c
author Tero Marttila <terom@fixme.fi>
Sat, 30 Aug 2008 19:13:15 +0300
changeset 49 10c7dce1a043
parent 37 f0188b445c84
permissions -rw-r--r--
autogenerate the memcache_test help output, and pipeline memcache requests
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "../cache.h"
#include "cache.h"
#include "req.h"
#include "op.h"
#include "engine.h"
#include "../common.h"

void _cache_req_free (struct cache_req *req) {
    assert(req->op == NULL);

    free(req->key_copy.buf);
    free(req);
}

struct cache_req *cache_req (struct cache *cache, const struct cache_key *key, cache_callback cb_func, void *cb_data) {
    struct cache_req *req = NULL;

    // calloc the req info
    if ((req = calloc(1, sizeof(*req))) == NULL)
        ERROR("calloc");
    
    // set up basic state
    req->cb_func = cb_func;
    req->cb_data = cb_data;
    req->is_write = 0;

    // copy the key
    if (key->length == 0)
        req->key_copy.length = strlen(key->buf) + 1;
    else
        req->key_copy.length = key->length;

    if (req->key_copy.length == 0)
        ERROR("zero-length key");

    if ((req->key_copy.buf = malloc(req->key_copy.length)) == NULL)
        ERROR("malloc");

    memcpy(req->key_copy.buf, key->buf, req->key_copy.length);
    
    req->key = &req->key_copy;

    // look for an existing cache_op for this key
    if ((req->op = cache_op_find(cache, req->key)) == NULL) {

        // none available, start a new cache op
        if (cache->engine->fn_op_start(cache, &req->op, req->key))
            goto error;
        
        // since we were the one that created it, we take care of writing it if needed
        req->is_write = 1;
        
    } else {
        // we found an existing cache_op, we can just use that
        
    }
    
    // engine may forget to update the op_ptr
    assert(req->op);
    
    // register
    if (cache_op_register(req->op, req))
        goto error;

    // hurray, we now have an active cache_op \o/
    return req;

error:
    // we are not associated with any op
    req->op = NULL;

    _cache_req_free(req);

    return NULL;
}

/*int _cache_req_notify (struct cache_req *req, enum cache_req_event event) { */
int _cache_req_notify (struct cache_req *req) {
    if (req->cb_func(req, req->cb_data)) {
        // XXX: handle errors
        assert(0);
    }
    
    return 0;
}

int cache_req_notify (struct cache_req *req) {
/*
    switch (req->op->state) {
        case OP_STATE_INVALID:
        case OP_STATE_LOOKUP:
            assert(0);

            break;
        
        case OP_STATE_HIT:
            if (_cache_req_notify(req, CACHE_EVENT_HIT))
                goto error;

            break;

        case OP_STATE_MISS:
            if (_cache_req_notify(req, CACHE_EVENT_MISS))
                goto error;
            
            break;
        
        case OP_STATE_WRITE:
            if (_cache_req_notify(req, req->is_write ? CACHE_EVENT_BEGIN_WRITE : CACHE_EVENT_BEGIN_READ))
                goto error;

            break;

        default:
            assert(0);

            break;
    }

    return 0;

error:
    return -1;
*/
    
    return _cache_req_notify(req);
}

enum cache_req_state cache_req_state (struct cache_req *req) {
     switch (req->op->state) {
        case OP_STATE_INVALID:
            return CACHE_STATE_INVALID;

        case OP_STATE_LOOKUP:
            return CACHE_STATE_LOOKUP;
        
        case OP_STATE_HIT:
            return CACHE_STATE_HIT;

        case OP_STATE_MISS:
            return req->is_write ? CACHE_STATE_MISS : CACHE_STATE_OPEN_READ;
        
        case OP_STATE_OPEN_READ:
            return CACHE_STATE_OPEN_READ;
        
        case OP_STATE_OPEN_WRITE:
            return req->is_write ? CACHE_STATE_OPEN_WRITE : CACHE_STATE_READ;

        case OP_STATE_READ:
            return CACHE_STATE_READ;

        case OP_STATE_WRITE:
            return req->is_write ? CACHE_STATE_WRITE : CACHE_STATE_READ;
        
        case OP_STATE_ERROR:
            // XXX: error handling
            assert(0);

        default:
            assert(0);
    }
}

const struct cache_key *cache_req_key (struct cache_req *req) {
    return req->key;
}

int cache_req_available (struct cache_req *req, size_t *size, size_t *offset, size_t *available) {
    if (req->op->state == OP_STATE_INVALID || req->op->state == OP_STATE_LOOKUP || req->op->state == OP_STATE_ERROR)
        ERROR("req is not readable");

    if (cache_op_available(req->op, size, offset))
        goto error;
    
    // this is based on our own read offset
    *available = (*offset - req->read_offset);

    return 0;

error:
    return -1;
}

int cache_req_begin_write(struct cache_req *req, size_t hint) {
    if (req->op->state != OP_STATE_MISS || !req->is_write)
        ERROR("req not in pre-write mode");

    return cache_op_begin_write(req->op, hint);

error:
    return -1;
}

int cache_req_begin_read (struct cache_req *req) {
    if (req->op->state == OP_STATE_HIT) {
        // needs to be done
        return cache_op_begin_read(req->op);

    } else if (req->op->state == OP_STATE_OPEN_READ || req->op->state == OP_STATE_READ) {
        WARNING("req is already in OP_STATE_READ/OP_STATE_OPEN_READ");

    } else if (req->op->state == OP_STATE_OPEN_WRITE || req->op->state == OP_STATE_WRITE) {
        WARNING("req is already in OP_STATE_WRITE/OP_STATE_OPEN_WRITE");

    } else {
        ERROR("req is not readable");
    }

    return 0;

error:
    return -1;
}

int cache_req_push (struct cache_req *req, int fd, size_t *size) {
    if (req->op->state != OP_STATE_WRITE || !req->is_write)
        ERROR("req not in write mode");
    
    return cache_op_push(req->op, fd, size); 

error:
    return -1;
}

int cache_req_pull (struct cache_req *req, int fd, size_t *size) {
    if (req->op->state != OP_STATE_READ && req->op->state != OP_STATE_WRITE)
        ERROR("req is not readable");

    return cache_op_pull(req->op, fd, &req->read_offset, size);

error:
    return -1;
}

int cache_req_done (struct cache_req *req) {
    if (req->op->state != OP_STATE_WRITE || !req->is_write)
        ERROR("req not in write mode");
    
    return cache_op_done(req->op); 

error:
    return -1;
}

void cache_req_release (struct cache_req *req) {
    // unconditional deregister
    cache_op_deregister(req->op, req);
    
    // we are not associated with any op anymore
    req->op = NULL;
    
    // free
    _cache_req_free(req);
}