struct Fs { RwLock>>, Mutex>, IndexedSparseFile } struct IndexedSparseFile { file: File, allocator: IndexAllocator } struct ContentId(Hash) // Conceptually: struct ChunkMap { VecMap } enum Chunk { Immutable(ContentId), Mutable(MutableChunk) } struct MutableChunk { Index, Version, Mutex> // None = Dirty, Some(id) = Stable(id) Condvar } struct ImmutableChunk { Index Mutex, Condvar } enum ImmutableChunkState { Stable, Evicted, Reserved } // Actually: struct ChunkMap { // Can use capn'proto for serde these RawChunks memory: Mmap, locks: VecMap } struct Signal { mutex: Mutex<()>, cond: Condvar } impl Signal { fn wait(&'a self, SignalGuard<'a>) -> SignalGuard<'a>; fn lock(&'a self) -> SignalGuard<'a>; fn notify_one(self); fn notify_all(self); } enum RawChunk { Immutable(ContentId), Mutable(Index, Version, Option) } // Exposes a similar (safe!) interface to ImmutableChunk above struct ImmutableChunk<'a> { data: *mut RawChunk::Immutable, signal: &'a Signal, } // Exposes a similar (safe!) interface to MutableChunk above struct MutableChunk<'a> { data: *mut RawChunk::Mutable, signal: &'a Signal, } // Operations get written in terms of the safe interface to ChunkMap // Read of immutable chunk proceeds like read of read-only blob today // Read of mutable chunk always succeeds // Write of immutable chunk causes a transition to mutable chunk by *copying* // the data to a new index and transitioning from Chunk::Immutable to // Chunk::Mutable // Evict happens by popping from lru, try_locking, and moving on to the next // candidate if try_lock fails