diff --git a/platform/indexing-impl/src/com/intellij/index/PrebuiltIndexProvider.kt b/platform/indexing-impl/src/com/intellij/index/PrebuiltIndexProvider.kt index b34ab7178f68..a76979d68452 100644 --- a/platform/indexing-impl/src/com/intellij/index/PrebuiltIndexProvider.kt +++ b/platform/indexing-impl/src/com/intellij/index/PrebuiltIndexProvider.kt @@ -6,6 +6,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.application.PathManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.registry.Registry import com.intellij.psi.stubs.FileContentHashing import com.intellij.psi.stubs.HashCodeDescriptor @@ -15,6 +16,7 @@ import com.intellij.util.indexing.FileContent import com.intellij.util.io.DataExternalizer import com.intellij.util.io.PersistentHashMap import java.io.File +import java.io.FileFilter import java.io.IOException import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.withLock @@ -46,10 +48,15 @@ abstract class PrebuiltIndexProvider: Disposable { internal fun init() { myOpenCloseLock.writeLock().withLock { if (USE_PREBUILT_INDEX) { - val indexesRoot = findPrebuiltIndicesRoot() + var indexesRoot = findPrebuiltIndicesRoot() try { if (indexesRoot != null && indexesRoot.exists()) { + // we should copy prebuilt indexes to a writable folder + indexesRoot = copyPrebuiltIndicesToIndexRoot(indexesRoot) + // otherwise we can get access denied error, because persistent hash map opens file for read and write + myPrebuiltIndexStorage = openIndexStorage(indexesRoot) + LOG.info("Using prebuilt $indexName from " + myPrebuiltIndexStorage?.baseFile?.toAbsolutePath()) } else { @@ -103,6 +110,15 @@ abstract class PrebuiltIndexProvider: Disposable { protected abstract fun getIndexRoot(): File + @Throws(IOException::class) + private fun copyPrebuiltIndicesToIndexRoot(prebuiltIndicesRoot: File): File { + val indexRoot = getIndexRoot() + + FileUtil.copyDir(prebuiltIndicesRoot, indexRoot, FileFilter { f -> f.name.startsWith(indexName) }) + + return indexRoot + } + private fun findPrebuiltIndicesRoot(): File? { val path: String? = System.getProperty(PrebuiltStubsProviderBase.PREBUILT_INDICES_PATH_PROPERTY) if (path != null && File(path).exists()) { diff --git a/platform/indexing-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt b/platform/indexing-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt index 23f3cfad4202..09197ba17546 100644 --- a/platform/indexing-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt +++ b/platform/indexing-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt @@ -106,7 +106,7 @@ abstract class PrebuiltStubsProviderBase : PrebuiltIndexProvider implements AppendablePersistentMap> recordHandler = myEnumerator.getRecordHandler(); + myParentValueRefOffset = recordHandler.getRecordBuffer(myEnumerator).length; + myIntMapping = valueExternalizer instanceof IntInlineKeyDescriptor && wantNonNegativeIntegralValues(); + myDirectlyStoreLongFileOffsetMode = keyDescriptor instanceof InlineKeyDescriptor && myEnumerator instanceof PersistentBTreeEnumerator; + + myRecordBuffer = ThreadLocal + .withInitial(() -> myDirectlyStoreLongFileOffsetMode ? ArrayUtilRt.EMPTY_BYTE_ARRAY : new byte[myParentValueRefOffset + 8]); + mySmallRecordBuffer = ThreadLocal + .withInitial(() -> myDirectlyStoreLongFileOffsetMode ? ArrayUtilRt.EMPTY_BYTE_ARRAY : new byte[myParentValueRefOffset + 4]); + + myEnumerator.setRecordHandler(new PersistentEnumeratorBase.RecordBufferHandler>() { + @Override + int recordWriteOffset(PersistentEnumeratorBase enumerator, byte[] buf) { + return recordHandler.recordWriteOffset(enumerator, buf); + } + + @Override + byte @NotNull [] getRecordBuffer(PersistentEnumeratorBase enumerator) { + return myIntAddressForNewRecord ? mySmallRecordBuffer.get() : myRecordBuffer.get(); + } + + @Override + void setupRecord(PersistentEnumeratorBase enumerator, int hashCode, int dataOffset, byte @NotNull [] buf) { + recordHandler.setupRecord(enumerator, hashCode, dataOffset, buf); + for (int i = myParentValueRefOffset; i < buf.length; i++) { + buf[i] = 0; + } + } + }); + + myEnumerator.setMarkCleanCallback( + new Flushable() { + @Override + public void flush() { + myEnumerator.putMetaData(myLiveAndGarbageKeysCounter); + myEnumerator.putMetaData2(myLargeIndexWatermarkId | ((long)myReadCompactionGarbageSize << 32)); + } + } + ); + + if (myDoTrace) LOG.info("Opened " + file); try { - myEnumerator = PersistentEnumeratorDelegate.createDefaultEnumerator(checkDataFiles(file), - keyDescriptor, - initialSize, - lockContext, - modifyVersionDependingOnOptions(version, options)); + myValueExternalizer = valueExternalizer; + myValueStorage = myIntMapping ? null : new PersistentHashMapValueStorage(getDataFile(file), options); + myAppendCache = myIntMapping ? null : createAppendCache(keyDescriptor); + myAppendCacheFlusher = myIntMapping ? null : LowMemoryWatcher.register(this::dropMemoryCaches); + myLiveAndGarbageKeysCounter = myEnumerator.getMetaData(); + long data2 = myEnumerator.getMetaData2(); + myLargeIndexWatermarkId = (int)(data2 & DEAD_KEY_NUMBER_MASK); + myReadCompactionGarbageSize = (int)(data2 >>> 32); + myCanReEnumerate = myEnumerator.canReEnumerate(); - myStorageFile = file; - myKeyDescriptor = keyDescriptor; - - final PersistentEnumeratorBase.@NotNull RecordBufferHandler> recordHandler = - myEnumerator.getRecordHandler(); - myParentValueRefOffset = recordHandler.getRecordBuffer(myEnumerator).length; - myIntMapping = valueExternalizer instanceof IntInlineKeyDescriptor && wantNonNegativeIntegralValues(); - myDirectlyStoreLongFileOffsetMode = keyDescriptor instanceof InlineKeyDescriptor && myEnumerator instanceof PersistentBTreeEnumerator; - - myRecordBuffer = ThreadLocal - .withInitial(() -> myDirectlyStoreLongFileOffsetMode ? ArrayUtilRt.EMPTY_BYTE_ARRAY : new byte[myParentValueRefOffset + 8]); - mySmallRecordBuffer = ThreadLocal - .withInitial(() -> myDirectlyStoreLongFileOffsetMode ? ArrayUtilRt.EMPTY_BYTE_ARRAY : new byte[myParentValueRefOffset + 4]); - - myEnumerator.setRecordHandler(new PersistentEnumeratorBase.RecordBufferHandler>() { - @Override - int recordWriteOffset(PersistentEnumeratorBase enumerator, byte[] buf) { - return recordHandler.recordWriteOffset(enumerator, buf); - } - - @Override - byte @NotNull [] getRecordBuffer(PersistentEnumeratorBase enumerator) { - return myIntAddressForNewRecord ? mySmallRecordBuffer.get() : myRecordBuffer.get(); - } - - @Override - void setupRecord(PersistentEnumeratorBase enumerator, int hashCode, int dataOffset, byte @NotNull [] buf) { - recordHandler.setupRecord(enumerator, hashCode, dataOffset, buf); - for (int i = myParentValueRefOffset; i < buf.length; i++) { - buf[i] = 0; - } - } - }); - - myEnumerator.setMarkCleanCallback( - new Flushable() { - @Override - public void flush() { - myEnumerator.putMetaData(myLiveAndGarbageKeysCounter); - myEnumerator.putMetaData2(myLargeIndexWatermarkId | ((long)myReadCompactionGarbageSize << 32)); - } - } - ); - - if (myDoTrace) LOG.info("Opened " + file); - try { - myValueExternalizer = valueExternalizer; - myValueStorage = myIntMapping ? null : new PersistentHashMapValueStorage(getDataFile(file), options); - myAppendCache = myIntMapping ? null : createAppendCache(keyDescriptor); - myAppendCacheFlusher = myIntMapping ? null : LowMemoryWatcher.register(this::dropMemoryCaches); - myLiveAndGarbageKeysCounter = myEnumerator.getMetaData(); - long data2 = myEnumerator.getMetaData2(); - myLargeIndexWatermarkId = (int)(data2 & DEAD_KEY_NUMBER_MASK); - myReadCompactionGarbageSize = (int)(data2 >>> 32); - myCanReEnumerate = myEnumerator.canReEnumerate(); - - if (!options.isReadOnly() && makesSenseToCompact()) { - compact(); - } - } - catch (IOException e) { - try { - // attempt to close already opened resources - close(); - } - catch (Throwable ignored) { - } - throw e; // rethrow - } - catch (Throwable t) { - LOG.error(t); - try { - // attempt to close already opened resources - close(); - } - catch (Throwable ignored) { - } - throw new PersistentEnumerator.CorruptedException(file); + if (!options.isReadOnly() && makesSenseToCompact()) { + compact(); } } - finally { - if (myIsReadOnly) { - PersistentHashMapValueStorage.CreationTimeOptions.READONLY.set(oldThreadLocalReadOnlyStatus); + catch (IOException e) { + try { + // attempt to close already opened resources + close(); } + catch (Throwable ignored) { + } + throw e; // rethrow + } + catch (Throwable t) { + LOG.error(t); + try { + // attempt to close already opened resources + close(); + } + catch (Throwable ignored) { + } + throw new PersistentEnumerator.CorruptedException(file); } } diff --git a/platform/util/src/com/intellij/util/io/ReadWriteDirectBufferWrapper.java b/platform/util/src/com/intellij/util/io/ReadWriteDirectBufferWrapper.java index 45c8ca03d5c8..46471e96060b 100644 --- a/platform/util/src/com/intellij/util/io/ReadWriteDirectBufferWrapper.java +++ b/platform/util/src/com/intellij/util/io/ReadWriteDirectBufferWrapper.java @@ -43,6 +43,7 @@ public class ReadWriteDirectBufferWrapper extends DirectBufferWrapper { static class FileContext implements AutoCloseable { private final FileChannel myFile; + private final boolean myReadOnly; FileContext(Path path, boolean readOnly) throws IOException { myFile = FileUtilRt.doIOOperation(new FileUtilRt.RepeatableIOOperation() { @@ -52,7 +53,7 @@ public class ReadWriteDirectBufferWrapper extends DirectBufferWrapper { @Override public FileChannel execute(boolean finalAttempt) throws IOException { try { - Set options = readOnly + Set options = myReadOnly ? EnumSet.of(StandardOpenOption.READ) : EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); return unInterruptible(FileChannel.open(path, options)); @@ -73,6 +74,7 @@ public class ReadWriteDirectBufferWrapper extends DirectBufferWrapper { } } }); + myReadOnly = readOnly; } @Override