don't like these Ref<>

This commit is contained in:
Alexey Kudravtsev
2016-12-19 14:32:27 +03:00
parent 0b8b9e9c2c
commit 15f812f4fe
3 changed files with 110 additions and 54 deletions
@@ -15,6 +15,9 @@
*/
package com.intellij.openapi.editor.event;
import com.intellij.util.ArrayFactory;
import org.jetbrains.annotations.NotNull;
import java.util.EventListener;
/**
@@ -27,6 +30,14 @@ import java.util.EventListener;
@SuppressWarnings("JavadocReference")
public interface DocumentListener extends EventListener{
DocumentListener[] EMPTY_ARRAY = new DocumentListener[0];
ArrayFactory<DocumentListener> ARRAY_FACTORY = new ArrayFactory<DocumentListener>() {
@NotNull
@Override
public DocumentListener[] create(int count) {
return count == 0 ? EMPTY_ARRAY : new DocumentListener[count];
}
};
/**
* Called before the text of the document is changed.
*
@@ -60,8 +60,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl");
private final Ref<DocumentListener[]> myCachedDocumentListeners = Ref.create(null);
private final List<DocumentListener> myDocumentListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final LockFreeCOWSortedArray<DocumentListener> myDocumentListeners = new LockFreeCOWSortedArray<DocumentListener>(PrioritizedDocumentListener.COMPARATOR, DocumentListener.ARRAY_FACTORY);
private final List<DocumentBulkUpdateListener> myBulkDocumentInternalListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final RangeMarkerTree<RangeMarkerEx> myRangeMarkers = new RangeMarkerTree<RangeMarkerEx>(this);
private final RangeMarkerTree<RangeMarkerEx> myPersistentRangeMarkers = new RangeMarkerTree<RangeMarkerEx>(this);
@@ -461,7 +460,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
@Override
public int getListenersCount() {
return myDocumentListeners.size();
return getListeners().length;
}
@Override
@@ -533,7 +532,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
}
private void fireMoveText(int start, int end, int newBase) {
for (DocumentListener listener : getCachedListeners()) {
for (DocumentListener listener : getListeners()) {
if (listener instanceof PrioritizedInternalDocumentListener) {
((PrioritizedInternalDocumentListener)listener).moveTextHappened(start, end, newBase);
}
@@ -722,7 +721,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
myChangeInProgress = true;
try {
DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced, initialStartOffset, initialOldLength);
doBeforeChangedUpdate(event);
beforeChangedUpdate(event);
myTextString = null;
ImmutableCharSequence prevText = myText;
myText = newText;
@@ -739,7 +738,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
return sequence.get();
}
private void doBeforeChangedUpdate(DocumentEvent event) {
private void beforeChangedUpdate(DocumentEvent event) {
Application app = ApplicationManager.getApplication();
if (app != null) {
FileDocumentManager manager = FileDocumentManager.getInstance();
@@ -753,7 +752,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
getLineSet(); // initialize line set to track changed lines
if (!ShutDownTracker.isShutdownHookRunning()) {
DocumentListener[] listeners = getCachedListeners();
DocumentListener[] listeners = getListeners();
for (int i = listeners.length - 1; i >= 0; i--) {
try {
listeners[i].beforeDocumentChange(event);
@@ -791,7 +790,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
setModificationStamp(newModificationStamp);
if (!ShutDownTracker.isShutdownHookRunning()) {
DocumentListener[] listeners = getCachedListeners();
DocumentListener[] listeners = getListeners();
for (DocumentListener listener : listeners) {
try {
listener.documentChanged(event);
@@ -865,43 +864,29 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
@Override
public void addDocumentListener(@NotNull DocumentListener listener) {
myCachedDocumentListeners.set(null);
if (myDocumentListeners.contains(listener)) {
if (ArrayUtil.contains(listener, getListeners())) {
LOG.error("Already registered: " + listener);
}
boolean added = myDocumentListeners.add(listener);
LOG.assertTrue(added, listener);
myDocumentListeners.add(listener);
}
@Override
public void addDocumentListener(@NotNull final DocumentListener listener, @NotNull Disposable parentDisposable) {
addDocumentListener(listener);
Disposer.register(parentDisposable, new DocumentListenerDisposable(listener, myCachedDocumentListeners, myDocumentListeners));
}
private static class DocumentListenerDisposable implements Disposable {
private final DocumentListener myListener;
private final Ref<DocumentListener[]> myCachedDocumentListenersRef;
private final List<DocumentListener> myDocumentListeners;
private DocumentListenerDisposable(@NotNull DocumentListener listener,
@NotNull Ref<DocumentListener[]> cachedDocumentListenersRef,
@NotNull List<DocumentListener> documentListeners) {
myListener = listener;
myCachedDocumentListenersRef = cachedDocumentListenersRef;
myDocumentListeners = documentListeners;
}
@Override
public void dispose() {
doRemoveDocumentListener(myListener, myCachedDocumentListenersRef, myDocumentListeners);
}
Disposer.register(parentDisposable, new Disposable() {
@Override
public void dispose() {
removeDocumentListener(listener);
}
});
}
@Override
public void removeDocumentListener(@NotNull DocumentListener listener) {
doRemoveDocumentListener(listener, myCachedDocumentListeners, myDocumentListeners);
boolean success = myDocumentListeners.remove(listener);
if (!success) {
LOG.error("Can't remove document listener (" + listener + "). Registered listeners: " + Arrays.toString(getListeners()));
}
}
void addInternalBulkModeListener(@NotNull DocumentBulkUpdateListener listener) {
@@ -912,16 +897,6 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
myBulkDocumentInternalListeners.remove(listener);
}
private static void doRemoveDocumentListener(@NotNull DocumentListener listener,
@NotNull Ref<DocumentListener[]> cachedDocumentListenersRef,
@NotNull List<DocumentListener> documentListeners) {
cachedDocumentListenersRef.set(null);
boolean success = documentListeners.remove(listener);
if (!success) {
LOG.error("Can't remove document listener (" + listener + "). Registered listeners: " + documentListeners);
}
}
@Override
public int getLineNumber(final int offset) {
return getLineSet().findLineIndex(offset);
@@ -962,16 +937,8 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
}
@NotNull
private DocumentListener[] getCachedListeners() {
DocumentListener[] cachedListeners = myCachedDocumentListeners.get();
if (cachedListeners == null) {
DocumentListener[] listeners = ArrayUtil.stripTrailingNulls(myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]));
Arrays.sort(listeners, PrioritizedDocumentListener.COMPARATOR);
cachedListeners = listeners;
myCachedDocumentListeners.set(cachedListeners);
}
return cachedListeners;
private DocumentListener[] getListeners() {
return myDocumentListeners.getArray();
}
@Override
@@ -0,0 +1,78 @@
/*
* Copyright 2000-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.editor.impl;
import com.intellij.util.ArrayFactory;
import com.intellij.util.ArrayUtil;
import com.intellij.util.concurrency.AtomicFieldUpdater;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
/**
* Maintains an atomic immutable array of listeners of type {@code T} in sorted order according to {@link #comparator}
* N.B. internal array is exposed for faster iterating listeners in to- and reverse order, so care should be taken for not mutating it by clients
*/
class LockFreeCOWSortedArray<T> {
@NotNull private final Comparator<? super T> comparator;
private final ArrayFactory<T> arrayFactory;
/** changed by {@link #UPDATER} only */
@SuppressWarnings("FieldMayBeFinal")
@NotNull private volatile T[] listeners;
private static final AtomicFieldUpdater<LockFreeCOWSortedArray, Object[]> UPDATER = AtomicFieldUpdater.forFieldOfType(LockFreeCOWSortedArray.class, Object[].class);
LockFreeCOWSortedArray(@NotNull Comparator<? super T> comparator, @NotNull ArrayFactory<T> arrayFactory) {
this.comparator = comparator;
this.arrayFactory = arrayFactory;
listeners = arrayFactory.create(0);
}
// returns true if changed
void add(@NotNull T listener) {
while (true) {
T[] oldListeners = listeners;
int i = insertionIndex(oldListeners, listener);
T[] newListeners = ArrayUtil.insert(oldListeners, i, listener);
if (UPDATER.compareAndSet(this, oldListeners, newListeners)) break;
}
}
boolean remove(@NotNull T listener) {
while (true) {
T[] oldListeners = listeners;
T[] newListeners = ArrayUtil.remove(oldListeners, listener, arrayFactory);
//noinspection ArrayEquality
if (oldListeners == newListeners) return false;
if (UPDATER.compareAndSet(this, oldListeners, newListeners)) break;
}
return true;
}
private int insertionIndex(@NotNull T[] elements, @NotNull T e) {
for (int i=0; i<elements.length; i++) {
T element = elements[i];
if (comparator.compare(e, element) < 0) {
return i;
}
}
return elements.length;
}
@NotNull
T[] getArray() {
return listeners;
}
}