rename/clean up states slightly and add lots of documentation
authorTero Marttila <terom@fixme.fi>
Sat, 09 Aug 2008 14:42:59 +0300
changeset 36 b4023990811e
parent 35 020fdab3c986
child 37 f0188b445c84
rename/clean up states slightly and add lots of documentation
cache.h
cache/engine.h
cache/engines/fs.c
cache/op.c
cache/op.h
cache/req.c
cache/req.h
cache_test.c
--- a/cache.h	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache.h	Sat Aug 09 14:42:59 2008 +0300
@@ -43,13 +43,16 @@
     CACHE_STATE_INVALID,
 
     CACHE_STATE_LOOKUP,
-    CACHE_STATE_WRITE_BEGIN,
-    CACHE_STATE_READ_BEGIN,
+    CACHE_STATE_HIT,
+    CACHE_STATE_MISS,
+
+    CACHE_STATE_OPEN_READ,
+    CACHE_STATE_READ,
+
+    CACHE_STATE_OPEN_WRITE,
     CACHE_STATE_WRITE,
     CACHE_STATE_WRITE_PAUSE,
-    CACHE_STATE_READ,
 
-    CACHE_STATE_DONE,
     CACHE_STATE_ERROR,
 };
 
@@ -128,7 +131,12 @@
 /*
  * Prepare this cache req for writing in the data. Hint, if nonzero, is used to pre-allocate resources for the entry.
  */
-int cache_req_begin_write(struct cache_req *req, size_t hint);
+int cache_req_begin_write (struct cache_req *req, size_t hint);
+
+/*
+ * Prepare this cache req for use with cache_req_pull.
+ */
+int cache_req_begin_read (struct cache_req *req);
 
 /*
  * Add some data into this cache entry, reading from the given fd. This is only valid for cache_req's in
--- a/cache/engine.h	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache/engine.h	Sat Aug 09 14:42:59 2008 +0300
@@ -17,10 +17,15 @@
     /*
      * Return some information about the available data.
      */
-    int (*fn_op_available) (struct cache_op *op, size_t *size, size_t *offset);
+    int (*fn_op_available) (struct cache_op *, size_t *size, size_t *offset);
+    
+    /*
+     * Prepare to read from this cache entry.
+     */
+    int (*fn_op_begin_read) (struct cache_op *);
 
     /*
-     * Prepare to write and possibly read this cache entry.
+     * Prepare to write to this cache entry.
      *
      * size_hint, if nonzero, provides a guess at the size of the cache entry that can be used to optimize stuff
      */
@@ -31,18 +36,18 @@
      * bytes should be read, or zero to just read a suitable amount. Should be updated to how many bytes were actually
      * written (may be zero if writes are currently paused).
      */
-    int (*fn_op_push) (struct cache_op *op, int fd, size_t *size);
+    int (*fn_op_push) (struct cache_op *, int fd, size_t *size);
 
     /*
      * No more calls to fn_op_push will take place. The cache entry now contains the complete data.
      */
-    int (*fn_op_done) (struct cache_op *op);
+    int (*fn_op_done) (struct cache_op *);
 
     /*
      * The op is not needed for any operations anymore. Free any resources and memory associated with this op,
      * including the op itself.
      */
-    int (*fn_op_close) (struct cache_op *op);
+    int (*fn_op_close) (struct cache_op *);
 };
 
 #endif /* CACHE_ENGINE_H */
--- a/cache/engines/fs.c	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache/engines/fs.c	Sat Aug 09 14:42:59 2008 +0300
@@ -158,7 +158,7 @@
         ERROR("calloc");
     
     // init it
-    if (cache_op_init(&op->base, cache, key))
+    if (_cache_op_init(&op->base, cache, key))
         goto error;
     
     // mark it as being in the lookup state...
@@ -181,7 +181,7 @@
     }
 
     // indicate that the key was found/not found
-    if (cache_op_lookup_done(&op->base, found))
+    if (_cache_op_lookup_done(&op->base, found))
         goto error;
     
     *op_ptr = &op->base;
@@ -232,7 +232,7 @@
         goto error;
     
     // great
-    if (cache_op_write_ready(&op->base))
+    if (_cache_op_write_ready(&op->base))
         goto error;
 
     // done
@@ -277,7 +277,7 @@
     *size_ptr = ret;
 
     // notify newly available data
-    if (cache_op_data_available(&op->base))
+    if (_cache_op_data_available(&op->base))
         goto error;
 
     // great
@@ -306,7 +306,7 @@
         goto error;
 
     // notify that data is complete
-    if (cache_op_write_done(&op->base))
+    if (_cache_op_write_done(&op->base))
         goto error;
 
     // great
--- a/cache/op.c	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache/op.c	Sat Aug 09 14:42:59 2008 +0300
@@ -22,7 +22,7 @@
         WARNING("fn_op_close failed");
 }
 
-int cache_op_init (struct cache_op *op, struct cache *cache, struct cache_key *key) {
+int _cache_op_init (struct cache_op *op, struct cache *cache, struct cache_key *key) {
     op->cache = cache;
     op->key = key;
     op->state = OP_STATE_INVALID;
@@ -83,19 +83,35 @@
     return op->cache->engine->fn_op_available(op, size, offset);
 }
 
+int cache_op_begin_read (struct cache_op *op) {
+    assert(op->state == OP_STATE_HIT);
+
+    op->state = OP_STATE_OPEN_READ;
+
+    return op->cache->engine->fn_op_begin_read(op);
+}
+
 int cache_op_begin_write (struct cache_op *op, size_t size_hint) {
+    assert(op->state == OP_STATE_MISS);
+
+    op->state = OP_STATE_OPEN_WRITE;
+
     return op->cache->engine->fn_op_begin_write(op, size_hint);
 }
 
 int cache_op_push (struct cache_op *op, int fd, size_t *size) {
+    assert(op->state == OP_STATE_WRITE);
+
     return op->cache->engine->fn_op_push(op, fd, size);
 }
 
 int cache_op_done (struct cache_op *op) {
+    assert(op->state == OP_STATE_WRITE);
+
     return op->cache->engine->fn_op_done(op);
 }
 
-int cache_op_lookup_done (struct cache_op *op, int found) {
+int _cache_op_lookup_done (struct cache_op *op, int found) {
     // modify state
     op->state = found ? OP_STATE_HIT : OP_STATE_MISS;
 
@@ -103,7 +119,7 @@
     return _cache_op_notify(op);
 }
 
-int cache_op_write_ready (struct cache_op *op) {
+int _cache_op_write_ready (struct cache_op *op) {
     // modify state
     op->state = OP_STATE_WRITE;
 
@@ -111,12 +127,12 @@
     return _cache_op_notify(op);
 }
 
-int cache_op_data_available (struct cache_op *op) {
+int _cache_op_data_available (struct cache_op *op) {
     // notify waiting reqs
     return _cache_op_notify(op);
 }
 
-int cache_op_write_done (struct cache_op *op) {
+int _cache_op_write_done (struct cache_op *op) {
     op->state = OP_STATE_READ;
 
     // notify waiting reqs
--- a/cache/op.h	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache/op.h	Sat Aug 09 14:42:59 2008 +0300
@@ -3,37 +3,83 @@
 
 #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,
-    OP_STATE_LOOKUP,
 
+    /*
+     * 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;
 };
 
 /*
- * Initialize the basic cache op state. Refcount is set to one.
+ * 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);
+int _cache_op_init(struct cache_op *op, struct cache *cache, struct cache_key *key);
 
 /*
- * Look for an existing cache_op in the given cache. Return NULL if not found, return pointer and increment
- * refcount if found.
+ * 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);
 
@@ -43,30 +89,88 @@
 int cache_op_register (struct cache_op *op, struct cache_req *req);
 
 /*
- * The given req is not interested in this op anymore. The op is removed if it was the only 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 writing data to it, size_hint can be used to preallocate resources
+ * 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);
+
+/*
+ * 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 that the key lookup completed
+ * Used by the engines to notify of state transitions
  */
-int cache_op_lookup_done (struct cache_op *op, int found);
 
 /*
- * in OP_STATE_WRITE
+ * 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_write_ready (struct cache_op *op);
-int cache_op_data_available (struct cache_op *op);
-int cache_op_write_done (struct cache_op *op);
+int _cache_op_lookup_done (struct cache_op *op, int found);
+
+/*
+ * 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 */
--- a/cache/req.c	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache/req.c	Sat Aug 09 14:42:59 2008 +0300
@@ -50,8 +50,8 @@
         // 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
+        
+        // since we were the one that created it, we take care of writing it if needed
         req->is_write = 1;
         
     } else {
@@ -139,17 +139,27 @@
             return CACHE_STATE_LOOKUP;
         
         case OP_STATE_HIT:
-            return CACHE_STATE_READ_BEGIN;
+            return CACHE_STATE_HIT;
 
         case OP_STATE_MISS:
-            return req->is_write ? CACHE_STATE_WRITE_BEGIN : CACHE_STATE_READ_BEGIN;
+            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);
     }
@@ -160,12 +170,13 @@
 }
 
 int cache_req_available (struct cache_req *req, size_t *size, size_t *offset, size_t *available) {
-    if (req->op->state != OP_STATE_READ && req->op->state != OP_STATE_WRITE)
+    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;
@@ -184,6 +195,27 @@
     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");
--- a/cache/req.h	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache/req.h	Sat Aug 09 14:42:59 2008 +0300
@@ -7,18 +7,27 @@
 #include "op.h"
 
 struct cache_req {
+    // a pointer to the relevant key
     struct cache_key *key;
-    struct cache_key key_copy;  // XXX: who keeps the copy around?
+
+    // a copy
+    // XXX: this should be moved to cache_op
+    struct cache_key key_copy;
     
+    // we are part of cache_op.req_list
     LIST_ENTRY(cache_req) node;
     
+    // the user callback
     cache_callback cb_func;
     void *cb_data;
     
+    // the op we are using
     struct cache_op *op;
-
+    
+    // were we the origional req that created the op, i.e. the one that handles open_write in case of OP_STATE_MISS?
     int is_write;
-
+    
+    // our own read offset into the cache entry.
     off_t read_offset;
 };
 
--- a/cache_test.c	Sat Aug 09 01:00:35 2008 +0300
+++ b/cache_test.c	Sat Aug 09 14:42:59 2008 +0300
@@ -29,13 +29,18 @@
 const char *cache_state_str (enum cache_req_state state) {
     switch (state) {
         case CACHE_STATE_INVALID:       return "INVALID";
+
         case CACHE_STATE_LOOKUP:        return "LOOKUP";
-        case CACHE_STATE_READ_BEGIN:    return "READ_BEGIN";
-        case CACHE_STATE_WRITE_BEGIN:   return "WRITE_BEGIN";
+        case CACHE_STATE_HIT:           return "HIT";
+        case CACHE_STATE_MISS:          return "MISS";
+
+        case CACHE_STATE_OPEN_READ:     return "OPEN_READ";
+        case CACHE_STATE_READ:          return "READ";
+
+        case CACHE_STATE_OPEN_WRITE:    return "OPEN_WRITE";
         case CACHE_STATE_WRITE:         return "WRITE";
         case CACHE_STATE_WRITE_PAUSE:   return "WRITE_PAUSE";
-        case CACHE_STATE_READ:          return "READ";
-        case CACHE_STATE_DONE:          return "DONE";
+
         case CACHE_STATE_ERROR:         return "ERROR";
         default:                        return "???";
     }