Rewrite WindowCache to be easier to follow and maintain
The integration of WindowCache, ByteWindow, PackFile and WindowCursor
was a spaghetti of code that was impossible for even the original
author (me) to follow. Due to the way the responsibility for the
PackFile's open RandomAccessFile "fd" was distributed between these
four classes I could no longer prove to myself that the fd wouldn't
be closed while it was being accessed by another thread.
This rewrite generalizes most of the cache logic into a new class,
OffsetCache. The hope is that we can later reuse this code to make
a rewrite of UnpackedObjectCache, which uses similiar caching rules
as WindowCache, but applies a different hash function. That rewrite
is deferred to another change, but is anticipated by this one.
The new OffsetCache class uses the Java 5 atomic APIs to create a
much more concurrent hash table than we had before. We can now
perform no-miss reads without taking any locks. Reads that do
miss acquire a lock in order to prevent concurrent threads from
performing duplicate work loading the same window from disk,
however concurrent reads of different windows is still permitted.
Due to the more concurrent nature of the OffsetCache, it is now
possible for the cache to temporarily overshoot its resource limits.
This is a small temporary overshoot that is roughly bounded by the
number of concurrent threads operating against the same cache.
The API of the ByteWindow subclasses is now simplified by removing
the base class of SoftReference. It was a horrible idea to pass
the byte[] or MappedByteBuffer down through the call stack when the
implementation knew what type it should be operating on. We now
instead use a more traditional OO pattern of allowing the subclass
to directly specify its referent.
Responsibility for the RandomAccessFile "fd" within PackFile is now
strictly within PackFile. Two open reference counts track how the
callers are using the fd, ensuring that the fd remains open, so long
as the caller has made the appropriate begin*() invocation prior
to data access. One counter, beginWindowCache() is exclusively
for the ByteWindows created by WindowCache. Another counter,
beginCopyRawData(), is exclusively for PackWriter's need to lock
the PackFile open while it performs object reuse.
To keep the code simple a WindowCache.reconfigure() now discards the
entire current cache, and creates a new one. That invalidates every
open file, and every open ByteWindow, and forces them to load again.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>