terom@31: #include terom@31: #include terom@31: #include terom@30: terom@30: #include "../cache.h" terom@31: #include "cache.h" terom@30: #include "req.h" terom@30: #include "op.h" terom@31: #include "engine.h" terom@31: #include "../common.h" terom@30: terom@31: void _cache_req_free (struct cache_req *req) { terom@33: assert(req->op == NULL); terom@33: terom@31: free(req->key_copy.buf); terom@31: free(req); terom@31: } terom@31: terom@31: struct cache_req *cache_req (struct cache *cache, const struct cache_key *key, cache_callback cb_func, void *cb_data) { terom@30: struct cache_req *req = NULL; terom@30: terom@30: // calloc the req info terom@30: if ((req = calloc(1, sizeof(*req))) == NULL) terom@30: ERROR("calloc"); terom@30: terom@30: // set up basic state terom@30: req->cb_func = cb_func; terom@30: req->cb_data = cb_data; terom@30: req->is_write = 0; terom@30: terom@31: // copy the key terom@31: if (key->length == 0) terom@31: req->key_copy.length = strlen(key->buf) + 1; terom@31: else terom@31: req->key_copy.length = key->length; terom@31: terom@31: if (req->key_copy.length == 0) terom@31: ERROR("zero-length key"); terom@31: terom@31: if ((req->key_copy.buf = malloc(req->key_copy.length)) == NULL) terom@31: ERROR("malloc"); terom@31: terom@31: memcpy(req->key_copy.buf, key->buf, req->key_copy.length); terom@31: terom@31: req->key = &req->key_copy; terom@31: terom@30: // look for an existing cache_op for this key terom@31: if ((req->op = cache_op_find(cache, req->key)) == NULL) { terom@30: terom@30: // none available, start a new cache op terom@31: if (cache->engine->fn_op_start(cache, &req->op, req->key)) terom@30: goto error; terom@36: terom@36: // since we were the one that created it, we take care of writing it if needed terom@30: req->is_write = 1; terom@30: terom@30: } else { terom@30: // we found an existing cache_op, we can just use that terom@30: terom@30: } terom@30: terom@31: // engine may forget to update the op_ptr terom@31: assert(req->op); terom@31: terom@30: // register terom@31: if (cache_op_register(req->op, req)) terom@30: goto error; terom@30: terom@30: // hurray, we now have an active cache_op \o/ terom@31: return req; terom@30: terom@30: error: terom@33: // we are not associated with any op terom@33: req->op = NULL; terom@33: terom@31: _cache_req_free(req); terom@30: terom@31: return NULL; terom@30: } terom@30: terom@31: /*int _cache_req_notify (struct cache_req *req, enum cache_req_event event) { */ terom@31: int _cache_req_notify (struct cache_req *req) { terom@31: if (req->cb_func(req, req->cb_data)) { terom@30: // XXX: handle errors terom@30: assert(0); terom@30: } terom@30: terom@30: return 0; terom@30: } terom@30: terom@30: int cache_req_notify (struct cache_req *req) { terom@31: /* terom@30: switch (req->op->state) { terom@30: case OP_STATE_INVALID: terom@30: case OP_STATE_LOOKUP: terom@30: assert(0); terom@30: terom@30: break; terom@30: terom@30: case OP_STATE_HIT: terom@30: if (_cache_req_notify(req, CACHE_EVENT_HIT)) terom@30: goto error; terom@30: terom@30: break; terom@30: terom@30: case OP_STATE_MISS: terom@30: if (_cache_req_notify(req, CACHE_EVENT_MISS)) terom@30: goto error; terom@30: terom@30: break; terom@30: terom@31: case OP_STATE_WRITE: terom@31: if (_cache_req_notify(req, req->is_write ? CACHE_EVENT_BEGIN_WRITE : CACHE_EVENT_BEGIN_READ)) terom@31: goto error; terom@31: terom@31: break; terom@31: terom@30: default: terom@30: assert(0); terom@30: terom@30: break; terom@30: } terom@30: terom@30: return 0; terom@30: terom@30: error: terom@30: return -1; terom@31: */ terom@31: terom@31: return _cache_req_notify(req); terom@30: } terom@30: terom@31: enum cache_req_state cache_req_state (struct cache_req *req) { terom@30: switch (req->op->state) { terom@30: case OP_STATE_INVALID: terom@30: return CACHE_STATE_INVALID; terom@30: terom@30: case OP_STATE_LOOKUP: terom@30: return CACHE_STATE_LOOKUP; terom@30: terom@30: case OP_STATE_HIT: terom@36: return CACHE_STATE_HIT; terom@31: terom@30: case OP_STATE_MISS: terom@36: return req->is_write ? CACHE_STATE_MISS : CACHE_STATE_OPEN_READ; terom@30: terom@36: case OP_STATE_OPEN_READ: terom@36: return CACHE_STATE_OPEN_READ; terom@36: terom@36: case OP_STATE_OPEN_WRITE: terom@36: return req->is_write ? CACHE_STATE_OPEN_WRITE : CACHE_STATE_READ; terom@36: terom@33: case OP_STATE_READ: terom@33: return CACHE_STATE_READ; terom@33: terom@31: case OP_STATE_WRITE: terom@31: return req->is_write ? CACHE_STATE_WRITE : CACHE_STATE_READ; terom@33: terom@36: case OP_STATE_ERROR: terom@36: // XXX: error handling terom@36: assert(0); terom@36: terom@30: default: terom@30: assert(0); terom@30: } terom@30: } terom@30: terom@31: const struct cache_key *cache_req_key (struct cache_req *req) { terom@31: return req->key; terom@31: } terom@31: terom@33: int cache_req_available (struct cache_req *req, size_t *size, size_t *offset, size_t *available) { terom@36: if (req->op->state == OP_STATE_INVALID || req->op->state == OP_STATE_LOOKUP || req->op->state == OP_STATE_ERROR) terom@33: ERROR("req is not readable"); terom@33: terom@33: if (cache_op_available(req->op, size, offset)) terom@33: goto error; terom@36: terom@36: // this is based on our own read offset terom@33: *available = (*offset - req->read_offset); terom@33: terom@33: return 0; terom@33: terom@33: error: terom@33: return -1; terom@33: } terom@33: terom@31: int cache_req_begin_write(struct cache_req *req, size_t hint) { terom@31: if (req->op->state != OP_STATE_MISS || !req->is_write) terom@31: ERROR("req not in pre-write mode"); terom@31: terom@31: return cache_op_begin_write(req->op, hint); terom@31: terom@31: error: terom@31: return -1; terom@31: } terom@31: terom@36: int cache_req_begin_read (struct cache_req *req) { terom@36: if (req->op->state == OP_STATE_HIT) { terom@36: // needs to be done terom@36: return cache_op_begin_read(req->op); terom@36: terom@36: } else if (req->op->state == OP_STATE_OPEN_READ || req->op->state == OP_STATE_READ) { terom@36: WARNING("req is already in OP_STATE_READ/OP_STATE_OPEN_READ"); terom@36: terom@36: } else if (req->op->state == OP_STATE_OPEN_WRITE || req->op->state == OP_STATE_WRITE) { terom@36: WARNING("req is already in OP_STATE_WRITE/OP_STATE_OPEN_WRITE"); terom@36: terom@36: } else { terom@36: ERROR("req is not readable"); terom@36: } terom@36: terom@36: return 0; terom@36: terom@36: error: terom@36: return -1; terom@36: } terom@36: terom@31: int cache_req_push (struct cache_req *req, int fd, size_t *size) { terom@31: if (req->op->state != OP_STATE_WRITE || !req->is_write) terom@31: ERROR("req not in write mode"); terom@31: terom@31: return cache_op_push(req->op, fd, size); terom@31: terom@31: error: terom@31: return -1; terom@31: } terom@31: terom@37: int cache_req_pull (struct cache_req *req, int fd, size_t *size) { terom@37: if (req->op->state != OP_STATE_READ && req->op->state != OP_STATE_WRITE) terom@37: ERROR("req is not readable"); terom@37: terom@37: return cache_op_pull(req->op, fd, &req->read_offset, size); terom@37: terom@37: error: terom@37: return -1; terom@37: } terom@37: terom@33: int cache_req_done (struct cache_req *req) { terom@33: if (req->op->state != OP_STATE_WRITE || !req->is_write) terom@33: ERROR("req not in write mode"); terom@33: terom@33: return cache_op_done(req->op); terom@33: terom@33: error: terom@33: return -1; terom@33: } terom@33: terom@33: void cache_req_release (struct cache_req *req) { terom@33: // unconditional deregister terom@33: cache_op_deregister(req->op, req); terom@33: terom@33: // we are not associated with any op anymore terom@33: req->op = NULL; terom@33: terom@33: // free terom@33: _cache_req_free(req); terom@33: } terom@33: