Commit cad70092 authored by Maxim.Mossienko's avatar Maxim.Mossienko
Browse files

split access lock into two: lightweight read one and lock for creating / disposing resource.

Here is some timing figures for creating / disposing resources
Opened for:com.intellij.openapi.vfs.impl.ZipHandler$1@1832c1d, 640 for 5193
Closed for:com.intellij.openapi.vfs.impl.ZipHandler$1@1832c1d, 352 for 2993
Opened for:com.intellij.util.io.PersistentHashMapValueStorage$1@584821, 672 for 5290
Closed for:com.intellij.util.io.PersistentHashMapValueStorage$1@584821, 464 for 6347
Opened for:com.intellij.util.io.PersistentHashMapValueStorage$2@8713f9, 576 for 3803
Closed for:com.intellij.util.io.PersistentHashMapValueStorage$2@8713f9, 544 for 7451
parent fe767cbe
Showing with 93 additions and 28 deletions
+93 -28
......@@ -15,28 +15,22 @@
*/
package com.intellij.util.io;
import com.intellij.util.containers.SLRUCache;
import com.intellij.util.containers.SLRUMap;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class FileAccessorCache<K, T> implements com.intellij.util.containers.hash.EqualityPolicy<K> {
private final SLRUCache<K, Handle<T>> myCache;
private final Object myLock = new Object();
/*@GuardedBy("myCacheLock")*/ private final SLRUMap<K, Handle<T>> myCache;
/*@GuardedBy("myCacheLock")*/ private final List<T> myElementsToBeDisposed = new ArrayList<T>();
private final Object myCacheLock = new Object();
private final Object myUpdateLock = new Object();
public FileAccessorCache(int protectedQueueSize, int probationalQueueSize) {
myCache = new SLRUCache<K, Handle<T>>(protectedQueueSize, probationalQueueSize, this) {
@NotNull
@Override
public final Handle<T> createValue(K path) {
try {
return new Handle<T>(createAccessor(path), FileAccessorCache.this);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
myCache = new SLRUMap<K, Handle<T>>(protectedQueueSize, probationalQueueSize, this) {
@Override
protected final void onDropFromCache(K key, Handle<T> value) {
value.release();
......@@ -49,16 +43,75 @@ public abstract class FileAccessorCache<K, T> implements com.intellij.util.conta
@NotNull
public final Handle<T> get(K key) {
synchronized (myLock) {
final Handle<T> value = myCache.get(key);
value.allocate();
return value;
Handle<T> cached = getIfCached(key);
if (cached != null) return cached;
synchronized (myUpdateLock) {
cached = getIfCached(key);
if (cached != null) return cached;
return createHandle(key);
}
}
//private static final int FACTOR = 0xF;
//private static final AtomicLong myCreateTime = new AtomicLong();
//private static final AtomicInteger myCreateRequests = new AtomicInteger();
//private static final AtomicInteger myCloseRequests = new AtomicInteger();
//private static final AtomicLong myCloseTime = new AtomicLong();
@NotNull
private Handle<T> createHandle(K key) {
Handle<T> cached;
try {
//long started = System.nanoTime();
cached = new Handle<T>(createAccessor(key), this);
//myCreateTime.addAndGet(System.nanoTime() - started);
//int l = myCreateRequests.incrementAndGet();
//if ((l & FACTOR) == 0) {
// System.out.println("Opened for:" + this + ", " + l + " for " + (myCreateTime.get() / 1000000));
//}
cached.allocate();
synchronized (myCacheLock) {
myCache.put(key, cached);
}
disposeInvalidAccessors();
return cached;
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private void disposeInvalidAccessors() {
List<T> fileAccessorsToBeDisposed;
synchronized (myCacheLock) {
if (myElementsToBeDisposed.isEmpty()) return;
fileAccessorsToBeDisposed = new ArrayList<T>(myElementsToBeDisposed);
myElementsToBeDisposed.clear();
}
//assert Thread.holdsLock(myUpdateLock);
//long started = System.nanoTime();
for (T t : fileAccessorsToBeDisposed) {
try {
disposeAccessor(t);
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
//myCloseTime.addAndGet(System.nanoTime() - started);
//int l = myCloseRequests.addAndGet(fileAccessorsToBeDisposed.size());
//if ((l & FACTOR) == 0) {
// System.out.println("Closed for:" + this + ", " + l + " for " + (myCloseTime.get() / 1000000));
//}
}
public Handle<T> getIfCached(K key) {
synchronized (myLock) {
final Handle<T> value = myCache.getIfCached(key);
synchronized (myCacheLock) {
final Handle<T> value = myCache.get(key);
if (value != null) {
value.allocate();
}
......@@ -67,14 +120,28 @@ public abstract class FileAccessorCache<K, T> implements com.intellij.util.conta
}
public boolean remove(K key) {
synchronized (myLock) {
return myCache.remove(key);
try {
synchronized (myCacheLock) {
return myCache.remove(key);
}
}
finally {
synchronized (myUpdateLock) {
disposeInvalidAccessors();
}
}
}
public void clear() {
synchronized (myLock) {
myCache.clear();
try {
synchronized (myCacheLock) {
myCache.clear();
}
}
finally {
synchronized (myUpdateLock) {
disposeInvalidAccessors();
}
}
}
......@@ -104,10 +171,8 @@ public abstract class FileAccessorCache<K, T> implements com.intellij.util.conta
public final void release() {
if (myRefCount.decrementAndGet() == 0) {
try {
myOwner.disposeAccessor(myFileAccessor);
} catch (IOException ex) {
throw new RuntimeException(ex);
synchronized (myOwner.myCacheLock) {
myOwner.myElementsToBeDisposed.add(myFileAccessor);
}
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment