synchronized by read-write lock

This commit is contained in:
Dmitry Avdeev
2012-06-09 12:38:58 +04:00
parent e28f99ef00
commit bc84d3b85f
@@ -30,6 +30,9 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.openapi.util.io.ByteSequence;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.concurrency.JBLock;
import com.intellij.util.concurrency.JBReentrantReadWriteLock;
import com.intellij.util.concurrency.LockFactory;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.io.PagedFileStorage;
import com.intellij.util.io.PersistentStringEnumerator;
@@ -88,7 +91,9 @@ public class FSRecords implements Forceable {
private static final int CORRUPTED_MAGIC = 0xabcf7f7f;
private static final String CHILDREN_ATT = "FsRecords.DIRECTORY_CHILDREN";
private static final Object lock = new Object();
private static final JBLock r;
private static final JBLock w;
private static volatile int ourLocalModificationCount = 0;
private static volatile boolean ourIsDisposed;
@@ -99,6 +104,10 @@ public class FSRecords implements Forceable {
static {
//noinspection ConstantConditions
assert HEADER_SIZE <= RECORD_SIZE;
JBReentrantReadWriteLock lock = LockFactory.createReadWriteLock();
r = lock.readLock();
w = lock.writeLock();
}
private static class DbConnection {
@@ -116,7 +125,8 @@ public class FSRecords implements Forceable {
private static boolean myCorrupted = false;
public static void connect() {
synchronized (lock) {
try {
w.lock();
if (!ourInitialized) {
init();
scanFreeRecords();
@@ -124,6 +134,9 @@ public class FSRecords implements Forceable {
ourInitialized = true;
}
}
finally {
w.unlock();
}
}
private static void scanFreeRecords() {
@@ -337,7 +350,8 @@ public class FSRecords implements Forceable {
}
public static void force() {
synchronized (lock) {
try {
w.lock();
if (myRecords != null) {
markClean();
}
@@ -348,12 +362,16 @@ public class FSRecords implements Forceable {
myRecords.force();
}
}
finally {
w.unlock();
}
}
public static void flushSome() {
if (!isDirty() || HeavyProcessLatch.INSTANCE.isRunning()) return;
synchronized (lock) {
try {
w.lock();
if (myFlushingFuture == null) {
return; // avoid NPE when close has already taken place
}
@@ -366,6 +384,9 @@ public class FSRecords implements Forceable {
myRecords.force();
}
}
finally {
w.unlock();
}
}
public static boolean isDirty() {
@@ -477,9 +498,13 @@ public class FSRecords implements Forceable {
}
public static long getCreationTimestamp() {
synchronized (lock) {
try {
r.lock();
return DbConnection.getTimestamp();
}
finally {
r.unlock();
}
}
private static ResizeableMappedFile getRecords() {
@@ -499,39 +524,43 @@ public class FSRecords implements Forceable {
}
public static int createRecord() {
synchronized (lock) {
try {
DbConnection.markDirty();
try {
w.lock();
DbConnection.markDirty();
final int free = DbConnection.getFreeRecord();
if (free == 0) {
final int filelength = (int)getRecords().length();
LOG.assertTrue(filelength % RECORD_SIZE == 0);
int newrecord = filelength / RECORD_SIZE;
DbConnection.cleanRecord(newrecord);
assert filelength + RECORD_SIZE == getRecords().length();
return newrecord;
}
else {
DbConnection.cleanRecord(free);
return free;
}
final int free = DbConnection.getFreeRecord();
if (free == 0) {
final int filelength = (int)getRecords().length();
LOG.assertTrue(filelength % RECORD_SIZE == 0);
int newrecord = filelength / RECORD_SIZE;
DbConnection.cleanRecord(newrecord);
assert filelength + RECORD_SIZE == getRecords().length();
return newrecord;
}
catch (Throwable e) {
throw DbConnection.handleError(e);
else {
DbConnection.cleanRecord(free);
return free;
}
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
public static void deleteRecordRecursively(int id) {
synchronized (lock) {
try {
incModCount(id);
doDeleteRecursively(id);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
try {
w.lock();
incModCount(id);
doDeleteRecursively(id);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
@@ -544,17 +573,19 @@ public class FSRecords implements Forceable {
}
private static void deleteRecord(final int id) {
synchronized (lock) {
try {
DbConnection.markDirty();
deleteContentAndAttributes(id);
try {
w.lock();
DbConnection.markDirty();
deleteContentAndAttributes(id);
DbConnection.cleanRecord(id);
addToFreeRecordsList(id);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
DbConnection.cleanRecord(id);
addToFreeRecordsList(id);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
@@ -583,8 +614,8 @@ public class FSRecords implements Forceable {
}
public static int[] listRoots() throws IOException {
synchronized (lock) {
DbConnection.markDirty();
try {
r.lock();
final DataInputStream input = readAttribute(1, CHILDREN_ATT);
if (input == null) return ArrayUtil.EMPTY_INT_ARRAY;
@@ -596,12 +627,14 @@ public class FSRecords implements Forceable {
input.readInt(); // Name
result[i] = input.readInt(); // Id
}
return result;
}
finally {
input.close();
}
return result;
}
finally {
r.unlock();
}
}
@@ -616,7 +649,8 @@ public class FSRecords implements Forceable {
}
public static int findRootRecord(String rootUrl) throws IOException {
synchronized (lock) {
try {
w.lock();
DbConnection.markDirty();
final int root = getNames().enumerate(rootUrl);
@@ -663,10 +697,14 @@ public class FSRecords implements Forceable {
return id;
}
finally {
w.unlock();
}
}
public static void deleteRootRecord(int id) throws IOException {
synchronized (lock) {
try {
w.lock();
DbConnection.markDirty();
final DataInputStream input = readAttribute(1, CHILDREN_ATT);
assert input != null;
@@ -705,80 +743,92 @@ public class FSRecords implements Forceable {
output.close();
}
}
finally {
w.unlock();
}
}
public static int[] list(int id) {
synchronized (lock) {
try {
final DataInputStream input = readAttribute(id, CHILDREN_ATT);
if (input == null) return ArrayUtil.EMPTY_INT_ARRAY;
try {
r.lock();
final DataInputStream input = readAttribute(id, CHILDREN_ATT);
if (input == null) return ArrayUtil.EMPTY_INT_ARRAY;
final int count = input.readInt();
final int[] result = ArrayUtil.newIntArray(count);
for (int i = 0; i < count; i++) {
result[i] = input.readInt();
}
input.close();
return result;
}
catch (Throwable e) {
throw DbConnection.handleError(e);
final int count = input.readInt();
final int[] result = ArrayUtil.newIntArray(count);
for (int i = 0; i < count; i++) {
result[i] = input.readInt();
}
input.close();
return result;
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
r.unlock();
}
}
public static Pair<String[],int[]> listAll(int parentId) {
synchronized (lock) {
try {
final DataInputStream input = readAttribute(parentId, CHILDREN_ATT);
if (input == null) return Pair.create(ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_INT_ARRAY);
final int count = input.readInt();
final int[] ids = ArrayUtil.newIntArray(count);
final String[] names = ArrayUtil.newStringArray(count);
for (int i = 0; i < count; i++) {
int id = input.readInt();
ids[i] = id;
names[i] = getName(id);
}
input.close();
return Pair.create(names, ids);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
public static Pair<String[],int[]> listAll(int parentId) {
try {
r.lock();
final DataInputStream input = readAttribute(parentId, CHILDREN_ATT);
if (input == null) return Pair.create(ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_INT_ARRAY);
final int count = input.readInt();
final int[] ids = ArrayUtil.newIntArray(count);
final String[] names = ArrayUtil.newStringArray(count);
for (int i = 0; i < count; i++) {
int id = input.readInt();
ids[i] = id;
names[i] = getName(id);
}
input.close();
return Pair.create(names, ids);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
r.unlock();
}
}
public static boolean wereChildrenAccessed(int id) {
try {
synchronized (lock) {
return findAttributePage(id, CHILDREN_ATT, false) != 0;
}
r.lock();
return findAttributePage(id, CHILDREN_ATT, false) != 0;
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
r.unlock();
}
}
public static void updateList(int id, int[] children) {
synchronized (lock) {
try {
DbConnection.markDirty();
final DataOutputStream record = writeAttribute(id, CHILDREN_ATT, false);
record.writeInt(children.length);
for (int child : children) {
if (child == id) {
LOG.error("Cyclic parent child relations");
}
else {
record.writeInt(child);
}
try {
w.lock();
DbConnection.markDirty();
final DataOutputStream record = writeAttribute(id, CHILDREN_ATT, false);
record.writeInt(children.length);
for (int child : children) {
if (child == id) {
LOG.error("Cyclic parent child relations");
}
else {
record.writeInt(child);
}
record.close();
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
record.close();
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
@@ -800,25 +850,31 @@ public class FSRecords implements Forceable {
}
public static int getModCount() {
synchronized (lock) {
try {
r.lock();
return getRecords().getInt(HEADER_GLOBAL_MOD_COUNT_OFFSET);
}
finally {
r.unlock();
}
}
public static int getParent(int id) {
synchronized (lock) {
try {
final int parentId = getRecordInt(id, PARENT_OFFSET);
if (parentId == id) {
LOG.error("Cyclic parent child relations in the database. id = " + id);
return 0;
}
try {
r.lock();
final int parentId = getRecordInt(id, PARENT_OFFSET);
if (parentId == id) {
LOG.error("Cyclic parent child relations in the database. id = " + id);
return 0;
}
return parentId;
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
return parentId;
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
r.unlock();
}
}
@@ -828,101 +884,129 @@ public class FSRecords implements Forceable {
return;
}
synchronized (lock) {
try {
incModCount(id);
putRecordInt(id, PARENT_OFFSET, parent);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
try {
w.lock();
incModCount(id);
putRecordInt(id, PARENT_OFFSET, parent);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
public static String getName(int id) {
synchronized (lock) {
try {
final int nameId = getRecordInt(id, NAME_OFFSET);
return nameId != 0 ? getNames().valueOf(nameId) : "";
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
try {
r.lock();
final int nameId = getRecordInt(id, NAME_OFFSET);
return nameId != 0 ? getNames().valueOf(nameId) : "";
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
r.unlock();
}
}
public static void setName(int id, String name) {
synchronized (lock) {
try {
incModCount(id);
putRecordInt(id, NAME_OFFSET, getNames().enumerate(name));
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
try {
w.lock();
incModCount(id);
putRecordInt(id, NAME_OFFSET, getNames().enumerate(name));
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
public static int getFlags(int id) {
synchronized (lock) {
try {
r.lock();
return getRecordInt(id, FLAGS_OFFSET);
}
finally {
r.unlock();
}
}
public static void setFlags(int id, int flags, final boolean markAsChange) {
synchronized (lock) {
try {
if (markAsChange) {
incModCount(id);
}
putRecordInt(id, FLAGS_OFFSET, flags);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
try {
w.lock();
if (markAsChange) {
incModCount(id);
}
putRecordInt(id, FLAGS_OFFSET, flags);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
public static long getLength(int id) {
synchronized (lock) {
try {
r.lock();
return getRecords().getLong(getOffset(id, LENGTH_OFFSET));
}
finally {
r.unlock();
}
}
public static void setLength(int id, long len) {
synchronized (lock) {
try {
incModCount(id);
getRecords().putLong(getOffset(id, LENGTH_OFFSET), len);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
try {
w.lock();
incModCount(id);
getRecords().putLong(getOffset(id, LENGTH_OFFSET), len);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
public static long getTimestamp(int id) {
synchronized (lock) {
try {
r.lock();
return getRecords().getLong(getOffset(id, TIMESTAMP_OFFSET));
}
finally {
r.unlock();
}
}
public static void setTimestamp(int id, long value) {
synchronized (lock) {
try {
incModCount(id);
getRecords().putLong(getOffset(id, TIMESTAMP_OFFSET), value);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
try {
w.lock();
incModCount(id);
getRecords().putLong(getOffset(id, TIMESTAMP_OFFSET), value);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
public static int getModCount(int id) {
synchronized (lock) {
try {
r.lock();
return getRecordInt(id, MOD_COUNT_OFFSET);
}
finally {
r.unlock();
}
}
private static void setModCount(int id, int value) {
@@ -961,10 +1045,14 @@ public class FSRecords implements Forceable {
public static DataInputStream readContent(int fileId) {
try {
int page;
synchronized (lock) {
try {
r.lock();
page = findContentPage(fileId, false);
if (page == 0) return null;
}
finally {
r.unlock();
}
return getContentStorage().readStream(page);
}
catch (Throwable e) {
@@ -987,10 +1075,14 @@ public class FSRecords implements Forceable {
try {
synchronized (attId) {
int page;
synchronized (lock) {
try {
r.lock();
page = findAttributePage(fileId, attId, false);
if (page == 0) return null;
}
finally {
r.unlock();
}
return getAttributesStorage().readStream(page);
}
}
@@ -1062,15 +1154,17 @@ public class FSRecords implements Forceable {
public static int acquireFileContent(int fileId) {
try {
synchronized (lock) {
int record = getContentRecordId(fileId);
if (record > 0) getContentStorage().acquireRecord(record);
return record;
}
w.lock();
int record = getContentRecordId(fileId);
if (record > 0) getContentStorage().acquireRecord(record);
return record;
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
w.unlock();
}
}
public static void releaseContent(int contentId) {
@@ -1084,13 +1178,15 @@ public class FSRecords implements Forceable {
public static int getContentId(int fileId) {
try {
synchronized (lock) {
return getContentRecordId(fileId);
}
r.lock();
return getContentRecordId(fileId);
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
r.unlock();
}
}
@NotNull
@@ -1191,10 +1287,14 @@ public class FSRecords implements Forceable {
public void writeBytes(ByteSequence bytes, int fileId) throws IOException {
final int page;
synchronized (lock) {
try {
w.lock();
incModCount(fileId);
page = findOrCreatePage();
}
finally {
w.unlock();
}
getStorage().writeBytes(page, bytes, myFixedSize);
}
@@ -1205,17 +1305,17 @@ public class FSRecords implements Forceable {
}
public static void dispose() {
synchronized (lock) {
try {
DbConnection.force();
DbConnection.closeFiles();
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
ourIsDisposed = true;
}
try {
w.lock();
DbConnection.force();
DbConnection.closeFiles();
}
catch (Throwable e) {
throw DbConnection.handleError(e);
}
finally {
ourIsDisposed = true;
w.unlock();
}
}
@@ -1226,7 +1326,8 @@ public class FSRecords implements Forceable {
public static void checkSanity() {
long t = System.currentTimeMillis();
synchronized (lock) {
try {
r.lock();
final int fileLength = (int)getRecords().length();
assert fileLength % RECORD_SIZE == 0;
int recordCount = fileLength / RECORD_SIZE;
@@ -1245,6 +1346,9 @@ public class FSRecords implements Forceable {
}
}
}
finally {
r.unlock();
}
t = System.currentTimeMillis() - t;
LOG.info("Sanity check took " + t + " ms");