/// <summary>Clear all entries related to a single file.</summary> /// <remarks> /// Clear all entries related to a single file. /// <p> /// Typically this method is invoked during /// <see cref="PackFile.Close()">PackFile.Close()</see> /// , when we /// know the pack is never going to be useful to us again (for example, it no /// longer exists on disk). A concurrent reader loading an entry from this /// same pack may cause the pack to become stuck in the cache anyway. /// </remarks> /// <param name="pack">the file to purge all entries of.</param> private void RemoveAll(PackFile pack) { for (int s = 0; s < tableSize; s++) { WindowCache.Entry e1 = table.Get(s); bool hasDead = false; for (WindowCache.Entry e = e1; e != null; e = e.next) { if ([email protected] == pack) { e.Kill(); hasDead = true; } else { if (e.dead) { hasDead = true; } } } if (hasDead) { table.CompareAndSet(s, e1, Clean(e1)); } } Gc(); }
private void Evict() { while (IsFull()) { int ptr = rng.Next(tableSize); WindowCache.Entry old = null; int slot = 0; for (int b = evictBatch - 1; b >= 0; b--, ptr++) { if (tableSize <= ptr) { ptr = 0; } for (WindowCache.Entry e = table.Get(ptr); e != null; e = e.next) { if (e.dead) { continue; } if (old == null || [email protected] < [email protected]) { old = e; slot = ptr; } } } if (old != null) { old.Kill(); Gc(); WindowCache.Entry e1 = table.Get(slot); table.CompareAndSet(slot, e1, Clean(e1)); } } }
private static WindowCache.Entry Clean(WindowCache.Entry top) { while (top != null && top.dead) { [email protected](); top = top.next; } if (top == null) { return(null); } WindowCache.Entry n = Clean(top.next); return(n == top.next ? top : new WindowCache.Entry(n, top.@ref)); }
/// <summary>Lookup a cached object, creating and loading it if it doesn't exist.</summary> /// <remarks>Lookup a cached object, creating and loading it if it doesn't exist.</remarks> /// <param name="pack">the pack that "contains" the cached object.</param> /// <param name="position">offset within <code>pack</code> of the object.</param> /// <returns>the object reference.</returns> /// <exception cref="System.IO.IOException"> /// the object reference was not in the cache and could not be /// obtained by /// <see cref="Load(PackFile, long)">Load(PackFile, long)</see> /// . /// </exception> private ByteWindow GetOrLoad(PackFile pack, long position) { int slot = Slot(pack, position); WindowCache.Entry e1 = table.Get(slot); ByteWindow v = Scan(e1, pack, position); if (v != null) { return(v); } lock (LockCache(pack, position)) { WindowCache.Entry e2 = table.Get(slot); if (e2 != e1) { v = Scan(e2, pack, position); if (v != null) { return(v); } } v = Load(pack, position); WindowCache.Ref @ref = CreateRef(pack, position, v); Hit(@ref); for (; ;) { WindowCache.Entry n = new WindowCache.Entry(Clean(e2), @ref); if (table.CompareAndSet(slot, e2, n)) { break; } e2 = table.Get(slot); } } if (evictLock.TryLock()) { try { Gc(); Evict(); } finally { evictLock.Unlock(); } } return(v); }
/// <summary>Clear every entry from the cache.</summary> /// <remarks> /// Clear every entry from the cache. /// <p> /// This is a last-ditch effort to clear out the cache, such as before it /// gets replaced by another cache that is configured differently. This /// method tries to force every cached entry through /// <see cref="Clear(Ref)">Clear(Ref)</see> /// to /// ensure that resources are correctly accounted for and cleaned up by the /// subclass. A concurrent reader loading entries while this method is /// running may cause resource accounting failures. /// </remarks> private void RemoveAll() { for (int s = 0; s < tableSize; s++) { WindowCache.Entry e1; do { e1 = table.Get(s); for (WindowCache.Entry e = e1; e != null; e = e.next) { e.Kill(); } }while (!table.CompareAndSet(s, e1, null)); } Gc(); }
private ByteWindow Scan(WindowCache.Entry n, PackFile pack, long position) { for (; n != null; n = n.next) { WindowCache.Ref r = n.@ref; if (r.pack == pack && r.position == position) { ByteWindow v = r.Get(); if (v != null) { Hit(r); return(v); } n.Kill(); break; } } return(null); }
private void Gc() { WindowCache.Ref r; while ((r = (WindowCache.Ref)queue.Poll()) != null) { // Sun's Java 5 and 6 implementation have a bug where a Reference // can be enqueued and dequeued twice on the same reference queue // due to a race condition within ReferenceQueue.enqueue(Reference). // // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6837858 // // We CANNOT permit a Reference to come through us twice, as it will // skew the resource counters we maintain. Our canClear() check here // provides a way to skip the redundant dequeues, if any. // if (r.CanClear()) { Clear(r); bool found = false; int s = Slot(r.pack, r.position); WindowCache.Entry e1 = table.Get(s); for (WindowCache.Entry n = e1; n != null; n = n.next) { if (n.@ref == r) { n.dead = true; found = true; break; } } if (found) { table.CompareAndSet(s, e1, Clean(e1)); } } } }
internal Entry(WindowCache.Entry n, WindowCache.Ref r) { next = n; @ref = r; }