EA-56355 - assert: PrioritizedDocumentListener$.getPriority; fix racey list.toArray(new T[list.size()])

This commit is contained in:
Alexey Kudravtsev
2014-12-08 14:05:21 +03:00
parent 8c4047d021
commit 7c837c1c29
9 changed files with 42 additions and 24 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@
package com.intellij.lang;
import com.intellij.psi.PsiFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@@ -53,7 +55,8 @@ public class CompositeLanguage extends Language {
return extensions.toArray(new Language[extensions.size()]);
}
@NotNull
public LanguageFilter[] getLanguageExtensions() {
return myFilters.toArray(new LanguageFilter[myFilters.size()]);
return ArrayUtil.stripTrailingNulls(myFilters.toArray(new LanguageFilter[myFilters.size()]));
}
}
@@ -34,10 +34,7 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.reference.SoftReference;
import com.intellij.util.DocumentUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.Processor;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.text.CharArrayUtil;
@@ -945,7 +942,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
private DocumentListener[] getCachedListeners() {
DocumentListener[] cachedListeners = myCachedDocumentListeners.get();
if (cachedListeners == null) {
DocumentListener[] listeners = myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]);
DocumentListener[] listeners = ArrayUtil.stripTrailingNulls(myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]));
Arrays.sort(listeners, PrioritizedDocumentListener.COMPARATOR);
cachedListeners = listeners;
myCachedDocumentListeners.set(cachedListeners);
@@ -189,12 +189,11 @@ public class VirtualFilePointerContainerImpl extends TraceableDisposable impleme
result = EMPTY;
}
else {
VirtualFilePointer[] vf = myList.toArray(new VirtualFilePointer[myList.size()]);
List<VirtualFile> cachedFiles = new ArrayList<VirtualFile>(vf.length);
List<String> cachedUrls = new ArrayList<String>(vf.length);
List<VirtualFile> cachedDirectories = new ArrayList<VirtualFile>(vf.length / 3);
List<VirtualFile> cachedFiles = new ArrayList<VirtualFile>(myList.size());
List<String> cachedUrls = new ArrayList<String>(myList.size());
List<VirtualFile> cachedDirectories = new ArrayList<VirtualFile>(myList.size() / 3);
boolean allFilesAreDirs = true;
for (VirtualFilePointer v : vf) {
for (VirtualFilePointer v : myList) {
VirtualFile file = v.getFile();
String url = v.getUrl();
cachedUrls.add(url);
@@ -43,10 +43,7 @@ import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.text.BlockSupportImpl;
import com.intellij.psi.text.BlockSupport;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.FileContentUtilCore;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.SystemProperties;
import com.intellij.util.*;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBus;
@@ -574,7 +571,8 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
@NotNull
public Document[] getUncommittedDocuments() {
ApplicationManager.getApplication().assertIsDispatchThread();
return myUncommittedDocuments.toArray(new Document[myUncommittedDocuments.size()]);
Document[] documents = myUncommittedDocuments.toArray(new Document[myUncommittedDocuments.size()]);
return ArrayUtil.stripTrailingNulls(documents);
}
boolean isInUncommittedSet(@NotNull Document document) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.Alarm;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
@@ -248,7 +249,7 @@ public class IntentionManagerImpl extends IntentionManager {
@Override
@NotNull
public IntentionAction[] getIntentionActions() {
return myActions.toArray(new IntentionAction[myActions.size()]);
return ArrayUtil.stripTrailingNulls(myActions.toArray(new IntentionAction[myActions.size()]));
}
@NotNull
@@ -37,6 +37,7 @@ import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerAdapter;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.EventDispatcher;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
@@ -241,7 +242,7 @@ public class EditorFactoryImpl extends EditorFactory implements ApplicationCompo
@Override
@NotNull
public Editor[] getAllEditors() {
return myEditors.toArray(new Editor[myEditors.size()]);
return ArrayUtil.stripTrailingNulls(myEditors.toArray(new Editor[myEditors.size()]));
}
@Override
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
@@ -853,4 +854,18 @@ public class ArrayUtil extends ArrayUtilRt {
dst[i++] = t;
}
}
@NotNull
public static <T> T[] stripTrailingNulls(T[] array) {
return array.length != 0 && array[array.length-1] == null ? Arrays.copyOf(array, trailingNullsIndex(array)) : array;
}
private static <T> int trailingNullsIndex(T[] array) {
for (int i=array.length-1; i>=0; i--) {
if (array[i] != null) {
return i+1;
}
}
return 0;
}
}
@@ -2242,6 +2242,9 @@ public class ContainerUtil extends ContainerUtilRt {
* - faster modification in the uncontended case
* - less memory
* - slower modification in highly contented case (which is the kind of situation you shouldn't use COWAL anyway)
*
* N.B. Avoid using <code>list.toArray(new T[list.size()])</code> on this list because it is inherently racey and
* therefore can return array with null elements at the end.
*/
@NotNull
@Contract(pure=true)
@@ -2330,6 +2333,9 @@ public class ContainerUtil extends ContainerUtilRt {
return new ConcurrentWeakHashMap<K, V>(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy);
}
/**
* @see {@link #createLockFreeCopyOnWriteList()}
*/
@NotNull
@Contract(pure=true)
public static <T> ConcurrentList<T> createConcurrentList() {
@@ -287,15 +287,13 @@ public class CvsEntriesManager extends VirtualFileAdapter {
}
private void onEntriesChanged(final VirtualFile parent) {
final CvsEntriesListener[] listeners = myEntriesListeners.toArray(new CvsEntriesListener[myEntriesListeners.size()]);
for (CvsEntriesListener listener : listeners) {
for (CvsEntriesListener listener : myEntriesListeners) {
listener.entriesChanged(parent);
}
}
private void onEntryChanged(final VirtualFile file) {
final CvsEntriesListener[] listeners = myEntriesListeners.toArray(new CvsEntriesListener[myEntriesListeners.size()]);
for (CvsEntriesListener listener : listeners) {
for (CvsEntriesListener listener : myEntriesListeners) {
listener.entryChanged(file);
}
}