public byte[] getBlock(long nr) throws IOException {
if (isClosed()) throw new IOException("FS closed (fs instance: " + this + ")");
// log.debug("blockCache size: "+blockCache.size());
int blockSize = superblock.getBlockSize();
Block result;
Integer key = Integer.valueOf((int) nr);
synchronized (blockCache) {
// check if the block has already been retrieved
if (blockCache.containsKey(key)) {
result = blockCache.get(key);
return result.getData();
}
}
// perform the time-consuming disk read outside of the synchronized
// block
// advantage:
// -the lock is held for a shorter time, so other blocks that are
// already in the cache can be returned immediately and
// do not have to wait for a long disk read
// disadvantage:
// -a single block can be retrieved more than once. However,
// the block will be put in the cache only once in the second
// synchronized block
ByteBuffer data = ByteBuffer.allocate(blockSize);
log.debug("Reading block " + nr + " (offset: " + nr * blockSize + ") from disk");
getApi().read(nr * blockSize, data);
// synchronize again
synchronized (blockCache) {
// check if the block has already been retrieved
if (!blockCache.containsKey(key)) {
result = new Block(this, nr, data.array());
blockCache.put(key, result);
return result.getData();
} else {
// it is important to ALWAYS return the block that is in
// the cache (it is used in synchronization)
result = blockCache.get(key);
return result.getData();
}
}
}