/* * 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; } }