/*MUST be called under the WriteLock*/ @NotNull private Map<Integer, SerializedStubTree> readOldData(final int key) throws StorageException { final Map<Integer, SerializedStubTree> result = new HashMap<Integer, SerializedStubTree>(); IndexStorage<Integer, SerializedStubTree> indexStorage = myStorage; if (indexStorage instanceof MemoryIndexStorage) { final MemoryIndexStorage<Integer, SerializedStubTree> memIndexStorage = (MemoryIndexStorage<Integer, SerializedStubTree>) indexStorage; if (!memIndexStorage.isBufferingEnabled()) { // if buffering is not enabled, use backend storage to make sure // the returned stub tree contains no data corresponding to unsaved documents. // This will ensure that correct set of old keys is used for update indexStorage = memIndexStorage.getBackendStorage(); } } try { final ValueContainer<SerializedStubTree> valueContainer = indexStorage.read(key); if (valueContainer.size() != 1) { LOG.assertTrue(valueContainer.size() == 0); return result; } result.put(key, valueContainer.getValueIterator().next()); return result; } catch (RuntimeException e) { final Throwable cause = e.getCause(); if (cause instanceof IOException) { throw new StorageException(cause); } throw e; } }
// need 'synchronized' to ensure atomic initialization of merged data // because several threads that acquired read lock may simultaneously execute the method private ValueContainerImpl<Value> getMergedData() { ValueContainerImpl<Value> merged = myMerged; if (merged != null) { return merged; } synchronized (myInitializer.getLock()) { merged = myMerged; if (merged != null) { return merged; } final ValueContainer<Value> fromDisk = myInitializer.compute(); final ValueContainerImpl<Value> newMerged; if (fromDisk instanceof ValueContainerImpl) { newMerged = ((ValueContainerImpl<Value>) fromDisk).copy(); } else { newMerged = ((ChangeTrackingValueContainer<Value>) fromDisk).getMergedData().copy(); } TIntHashSet invalidated = myInvalidated; if (invalidated != null) { invalidated.forEach( new TIntProcedure() { @Override public boolean execute(int inputId) { newMerged.removeAssociatedValue(inputId); return true; } }); } ValueContainerImpl<Value> added = myAdded; if (added != null) { added.forEach( new ContainerAction<Value>() { @Override public boolean perform(final int id, final Value value) { newMerged.removeAssociatedValue( id); // enforcing "one-value-per-file for particular key" invariant newMerged.addValue(id, value); return true; } }); } setNeedsCompacting(fromDisk.needsCompacting()); myMerged = newMerged; return newMerged; } }
private void saveImpl( final DataOutput out, final ValueContainer<T> container, final boolean asRemovedData) throws IOException { DataInputOutputUtil.writeSINT(out, container.size()); for (final Iterator<T> valueIterator = container.getValueIterator(); valueIterator.hasNext(); ) { final T value = valueIterator.next(); myExternalizer.save(out, value); final ValueContainer.IntIterator ids = container.getInputIdsIterator(value); if (ids != null) { DataInputOutputUtil.writeSINT(out, ids.size()); while (ids.hasNext()) { final int id = ids.next(); DataInputOutputUtil.writeSINT(out, asRemovedData ? -id : id); } } else { DataInputOutputUtil.writeSINT(out, 0); } } }
@Override protected void doPut(Key key, ValueContainer<Value> container) throws IOException { synchronized (myEnumerator) { ChangeTrackingValueContainer<Value> valueContainer = (ChangeTrackingValueContainer<Value>) container; if (!valueContainer.needsCompacting()) { final BufferExposingByteArrayOutputStream bytes = new BufferExposingByteArrayOutputStream(); //noinspection IOResourceOpenedButNotSafelyClosed final DataOutputStream _out = new DataOutputStream(bytes); final TIntHashSet set = valueContainer.getInvalidated(); if (set.size() > 0) { for (int inputId : set.toArray()) { ValueContainerExternalizer.saveInvalidateCommand(_out, inputId); } } final ValueContainer<Value> toRemove = valueContainer.getRemovedDelta(); if (toRemove.size() > 0) { myValueContainerExternalizer.saveAsRemoved(_out, toRemove); } final ValueContainer<Value> toAppend = valueContainer.getAddedDelta(); if (toAppend.size() > 0) { myValueContainerExternalizer.save(_out, toAppend); } appendData( key, new PersistentHashMap.ValueDataAppender() { public void append(final DataOutput out) throws IOException { out.write(bytes.getInternalBuffer(), 0, bytes.size()); } }); } else { // rewrite the value container for defragmentation super.doPut(key, valueContainer); } } }
@Override public <Key, Psi extends PsiElement> Collection<Psi> get( @NotNull final StubIndexKey<Key, Psi> indexKey, @NotNull final Key key, final Project project, final GlobalSearchScope scope) { FileBasedIndex.getInstance().ensureUpToDate(StubUpdatingIndex.INDEX_ID, project, scope); final PersistentFS fs = (PersistentFS) ManagingFS.getInstance(); final PsiManager psiManager = PsiManager.getInstance(project); final List<Psi> result = new ArrayList<Psi>(); final MyIndex<Key> index = (MyIndex<Key>) myIndices.get(indexKey); try { try { // disable up-to-date check to avoid locks on attempt to acquire index write lock while // holding at the same time the readLock for this index FileBasedIndex.disableUpToDateCheckForCurrentThread(); index.getReadLock().lock(); final ValueContainer<TIntArrayList> container = index.getData(key); container.forEach( new ValueContainer.ContainerAction<TIntArrayList>() { @Override public void perform(final int id, final TIntArrayList value) { final VirtualFile file = IndexInfrastructure.findFileByIdIfCached(fs, id); if (file != null && (scope == null || scope.contains(file))) { StubTree stubTree = null; final PsiFile _psifile = psiManager.findFile(file); PsiFileWithStubSupport psiFile = null; if (_psifile != null && !(_psifile instanceof PsiPlainTextFile)) { if (_psifile instanceof PsiFileWithStubSupport) { psiFile = (PsiFileWithStubSupport) _psifile; stubTree = psiFile.getStubTree(); if (stubTree == null && psiFile instanceof PsiFileImpl) { stubTree = ((PsiFileImpl) psiFile).calcStubTree(); } } } if (stubTree != null || psiFile != null) { if (stubTree == null) { stubTree = StubTreeLoader.getInstance().readFromVFile(project, file); if (stubTree != null) { final List<StubElement<?>> plained = stubTree.getPlainList(); for (int i = 0; i < value.size(); i++) { final StubElement<?> stub = plained.get(value.get(i)); final ASTNode tree = psiFile.findTreeForStub(stubTree, stub); if (tree != null) { if (tree.getElementType() == stubType(stub)) { result.add((Psi) tree.getPsi()); } else { String persistedStubTree = ((PsiFileStubImpl) stubTree.getRoot()).printTree(); String stubTreeJustBuilt = ((PsiFileStubImpl) ((IStubFileElementType) ((PsiFileImpl) psiFile).getContentElementType()) .getBuilder() .buildStubTree(psiFile)) .printTree(); StringBuilder builder = new StringBuilder(); builder.append("Oops\n"); builder.append("Recorded stub:-----------------------------------\n"); builder.append(persistedStubTree); builder.append( "\nAST built stub: ------------------------------------\n"); builder.append(stubTreeJustBuilt); builder.append("\n"); LOG.info(builder.toString()); // requestReindex() may want to acquire write lock (for indices not // requiring content loading) // thus, because here we are under read lock, need to use invoke later ApplicationManager.getApplication() .invokeLater( new Runnable() { @Override public void run() { FileBasedIndex.getInstance().requestReindex(file); } }, ModalityState.NON_MODAL); } } } } } else { final List<StubElement<?>> plained = stubTree.getPlainList(); for (int i = 0; i < value.size(); i++) { result.add((Psi) plained.get(value.get(i)).getPsi()); } } } } } }); } finally { index.getReadLock().unlock(); FileBasedIndex.enableUpToDateCheckForCurrentThread(); } } catch (StorageException e) { forceRebuild(e); } catch (RuntimeException e) { final Throwable cause = FileBasedIndex.getCauseToRebuildIndex(e); if (cause != null) { forceRebuild(cause); } else { throw e; } } return result; }