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