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