#ifndef CACHE_OP_H
#define CACHE_OP_H
#include <sys/queue.h>
// this duplicates the cache_state enum, but is indeed interpreted differently:
enum cache_op_state {
/*
* The op is still in the state of being constructed, and it not yet valid.
*
* This should not be encountered other than if the callback is called from within fn_op_start.
*/
OP_STATE_INVALID,
/*
* In the process of looking up the key. The op should enter this state as soon as it is constructed.
*/
OP_STATE_LOOKUP,
/*
* The key was not found in the cache. You should call cache_op_begin_write next.
*/
OP_STATE_MISS,
/*
* The key was found in the cache. You should call cache_op_begin_read next.
*/
OP_STATE_HIT,
/*
* cache_op_begin_read has been called, but the op is not yet read for cache_op_pull.
*/
OP_STATE_OPEN_READ,
/*
* cache_op_begin_write has been called, but the op is not yet read for cache_op_push.
*/
OP_STATE_OPEN_WRITE,
/*
* cache_op_begin_read has completed succesfully, and you may now call cache_op_pull.
*/
OP_STATE_READ,
/*
* cache_op_begin_write has completed succesfully, and you may now call cache_op_push/cache_op_done.
*/
OP_STATE_WRITE,
/*
* XXX: not used yet
*/
OP_STATE_ERROR,
};
struct cache_op {
// reference to what cache this belongs to
struct cache *cache;
// used to store this in the cache->op_list
LIST_ENTRY(cache_op) node;
// a pointer to the key we are using
// XXX: object lifetime is currently broken (cache_req.key_copy of the first req atm)
// XXX: the key should probably be copied into this.
struct cache_key *key;
// the list of cache_req's that are using this op.
LIST_HEAD(cache_op_req_list_head, cache_req) req_list;
// our current state.
enum cache_op_state state;
};
/*
* Used by the engine's fn_op_start implementation to initialize the basic cache op state.
*/
int _cache_op_init(struct cache_op *op, struct cache *cache, struct cache_key *key);
/*
* Look for an existing cache_op with the given key in the given cache. Return NULL if not found, return pointer
* if found.
*/
struct cache_op *cache_op_find (struct cache *cache, struct cache_key *key);
/*
* Add the given cache_req to the list of reqs using this op
*/
int cache_op_register (struct cache_op *op, struct cache_req *req);
/*
* The given req is done and not using this op anymore. The op is closed if it was the only req left
*
* The op would normally be in the OP_STATE_READ state.
*/
int cache_op_deregister (struct cache_op *op, struct cache_req *req);
/*
* Get info about the data available in this cache entry.
* size - total size of the cache entry. 0 if unknown (not yet fully written).
* offset - how many bytes of the cache entry are currently stored. May be 0
*
* size will be nonzero in the (OP_STATE_HIT | (OP_STATE_MISS -> OP_STATE_WRITE) -> ) OP_STATE_READ state.
*
* This is valid in all states except OP_STATE_INVALID, OP_STATE_LOOKUP and OP_STATE_ERROR, but both values may very
* well be zero in many of these states.
*/
int cache_op_available (struct cache_op *op, size_t *size, size_t *offset);
/*
* Prepare op for reading the data from it.
*
* The op must be in the OP_SATE_HIT state, and will move ino the OP_STATE_OPEN_READ state, possibly also
* directly into the OP_STATE_READ state.
*/
int cache_op_begin_read (struct cache_op *op);
/*
* Prepare op for writing data to it. If size_hint is nonzero, it is used to optimize resource preallocation.
*
* The op must be in the OP_STATE_MISS state, and will move into the OP_STATE_OPEN_WRITE state, possibly also
* directly into the OP_STATE_WRITE state.
*/
int cache_op_begin_write (struct cache_op *op, size_t size_hint);
/*
* Write some data into the cache by reading from the given fd. Size, if nonzero, tells how many bytes of data to read,
* otherwise, as much data as possible will be read. Size will be updated to the number of bytes actually pushed on
* return. This may be zero bytes (XXX: OP_STATE_PAUSE_WRITE).
*
* The op must be in the OP_STATE_WRITE state. All registered reqs will be notified, even if zero bytes were pushed.
*/
int cache_op_push (struct cache_op *op, int fd, size_t *size);
/*
* Read some data from the cache into the given fd.
*
* The data pulled from the cache entry should be *size bytes, starting at *offset. If the value of *size is zero,
* then as much data as is available will be pulled, otherwise, pull the given number of bytes. If the cache entry
* contains less bytes than requested, this is an error. Offset should be updated to point to what will be the next
* unread data, and size should be updated to how many bytes were read (if it was given as zero).
*
* The op mustbe in the OP_STATE_READ or OP_STATE_WRITE state.
*/
int cache_op_pull (struct cache_op *op, int fd, size_t *offset, size_t *size);
/*
* Indicate that the freshly written cache entry is now complete. This should be called after the last cache_op_push
* call, and no more cache_op_push calls may ensue.
*
* The op must be in the OP_STATE_WRITE state, and will move into the OP_STATE_READ state (i.e. the op may still be
* read from).
*/
int cache_op_done (struct cache_op *op);
/*
* Used by the engines to notify of state transitions
*/
/*
* cache_op_start completed, and the cache op is ready for begin_read/begin_write, based on `found`.
*
* OP_STATE_LOOKUP -> (OP_STATE_HIT | OP_STATE_MISS)
*/
int _cache_op_lookup_done (struct cache_op *op, int found);
/*
* cache_op_begin_read completed, ad the cache op is ready for cache_op_pull
*
* OP_STATE_OPEN_READ -> OP_STATE_READ
*/
int _cache_op_read_ready (struct cache_op *op);
/*
* cache_op_begin_write completed, and the cache op is ready for cache_op_push.
*
* OP_STATE_OPEN_WRITE -> OP_STATE_WRITE
*/
int _cache_op_write_ready (struct cache_op *op);
/*
* There is new data available for reading.
*
* OP_STATE_READ | OP_STATE_WRITE
*/
int _cache_op_data_available (struct cache_op *op);
/*
* cache_op_done completed, and the cache op now contains all the data that it ever will.
*
* OP_STATE_WRITE -> OP_STATE_READ
*/
int _cache_op_write_done (struct cache_op *op);
#endif /* CACHE_OP_H */