blob: 1cd3736d829bc84f5dcc93ed8798553ab2ba9ef0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2016 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.nd.db;
public final class ChunkCache {
private static ChunkCache sSharedInstance= new ChunkCache();
private Chunk[] fPageTable;
private boolean fTableIsFull;
private int fPointer;
public static ChunkCache getSharedInstance() {
return sSharedInstance;
}
public ChunkCache() {
this(5 * 1024 * 1024);
}
public ChunkCache(long maxSize) {
this.fPageTable= new Chunk[computeLength(maxSize)];
}
public synchronized void add(Chunk chunk, boolean locked) {
if (locked) {
chunk.fLocked= true;
}
if (chunk.fCacheIndex >= 0) {
chunk.fCacheHitFlag= true;
return;
}
if (this.fTableIsFull) {
evictChunk();
chunk.fCacheIndex= this.fPointer;
this.fPageTable[this.fPointer]= chunk;
} else {
chunk.fCacheIndex= this.fPointer;
this.fPageTable[this.fPointer]= chunk;
this.fPointer++;
if (this.fPointer == this.fPageTable.length) {
this.fPointer= 0;
this.fTableIsFull= true;
}
}
}
/**
* Evicts a chunk from the page table and the chunk table.
* After this method returns, {@link #fPointer} will contain
* the index of the evicted chunk within the page table.
*/
private void evictChunk() {
/*
* Use the CLOCK algorithm to determine which chunk to evict.
* i.e., if the chunk in the current slot of the page table has been
* recently referenced (i.e. the reference flag is set), unset the
* reference flag and move to the next slot. Otherwise, evict the
* chunk in the current slot.
*/
while (true) {
Chunk chunk = this.fPageTable[this.fPointer];
if (chunk.fCacheHitFlag) {
chunk.fCacheHitFlag= false;
this.fPointer= (this.fPointer + 1) % this.fPageTable.length;
} else {
chunk.fDatabase.releaseChunk(chunk);
chunk.fCacheIndex= -1;
this.fPageTable[this.fPointer] = null;
return;
}
}
}
public synchronized void remove(Chunk chunk) {
final int idx= chunk.fCacheIndex;
if (idx >= 0) {
if (this.fTableIsFull) {
this.fPointer= this.fPageTable.length-1;
this.fTableIsFull= false;
} else {
this.fPointer--;
}
chunk.fCacheIndex= -1;
final Chunk move= this.fPageTable[this.fPointer];
this.fPageTable[idx]= move;
move.fCacheIndex= idx;
this.fPageTable[this.fPointer]= null;
}
}
/**
* Returns the maximum size of the chunk cache in bytes.
*/
public synchronized long getMaxSize() {
return (long) this.fPageTable.length * Database.CHUNK_SIZE;
}
/**
* Clears the page table and changes it to hold chunks with
* maximum total memory of <code>maxSize</code>.
* @param maxSize the total size of the chunks in bytes.
*/
public synchronized void setMaxSize(long maxSize) {
final int newLength= computeLength(maxSize);
final int oldLength= this.fTableIsFull ? this.fPageTable.length : this.fPointer;
if (newLength > oldLength) {
Chunk[] newTable= new Chunk[newLength];
System.arraycopy(this.fPageTable, 0, newTable, 0, oldLength);
this.fTableIsFull= false;
this.fPointer= oldLength;
this.fPageTable= newTable;
} else {
for (int i= newLength; i < oldLength; i++) {
final Chunk chunk= this.fPageTable[i];
chunk.fDatabase.releaseChunk(chunk);
chunk.fCacheIndex= -1;
}
Chunk[] newTable= new Chunk[newLength];
System.arraycopy(this.fPageTable, 0, newTable, 0, newLength);
this.fTableIsFull= true;
this.fPointer= 0;
this.fPageTable= newTable;
}
}
private int computeLength(long maxSize) {
long maxLength= Math.min(maxSize / Database.CHUNK_SIZE, Integer.MAX_VALUE);
return Math.max(1, (int) maxLength);
}
}