struct Fs {
RwLock<HashMap<VolumeId, RwLock<ChunkMap>>>,
Mutex<LinkedHashMap<ContentId, ImmutableChunk>>,
IndexedSparseFile
}
struct IndexedSparseFile {
file: File,
allocator: IndexAllocator
}
struct ContentId(Hash)
// Conceptually:
struct ChunkMap {
VecMap<Chunk>
}
enum Chunk {
Immutable(ContentId),
Mutable(MutableChunk)
}
struct MutableChunk {
Index,
Version,
Mutex<Option<ContentId>> // None = Dirty, Some(id) = Stable(id)
Condvar
}
struct ImmutableChunk {
Index
Mutex<ImmutableChunkState>,
Condvar
}
enum ImmutableChunkState {
Stable,
Evicted,
Reserved
}
// Actually:
struct ChunkMap {
// Can use capn'proto for serde these RawChunks
memory: Mmap<RawChunk>,
locks: VecMap<Signal>
}
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<ContentId>)
}
// 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