cache/op.h
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
#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 */