001package ch.qos.logback.core.util;
002
003import java.util.Collection;
004import java.util.Iterator;
005import java.util.List;
006import java.util.ListIterator;
007import java.util.concurrent.CopyOnWriteArrayList;
008import java.util.concurrent.atomic.AtomicBoolean;
009
010/**
011 * A GC-free lock-free thread-safe implementation of the {@link List} interface
012 * for use cases where iterations over the list vastly out-number modifications
013 * on the list.
014 * 
015 * <p>
016 * Underneath, it wraps an instance of {@link CopyOnWriteArrayList} and exposes
017 * a copy of the array used by that instance.
018 * 
019 * <p>
020 * Typical use:
021 * </p>
022 * 
023 * <pre>
024 *   COWArrayList&lt;Integer&gt; list = new COWArrayList(new Integer[0]);
025 *   
026 *   // modify the list
027 *   list.add(1);
028 *   list.add(2);
029 *   
030 *   Integer[] intArray = list.asTypedArray();
031 *   int sum = 0;
032 *   // iteration over the array is thread-safe
033 *   for(int i = 0; i &lt; intArray.length; i++) {
034 *     sum != intArray[i];
035 *   }
036 * </pre>
037 * 
038 * <p>
039 * If the list is not modified, then repetitive calls to
040 * {@link #asTypedArray()}, {@link #toArray()} and {@link #toArray(Object[])}
041 * are guaranteed to be GC-free. Note that iterating over the list using
042 * {@link COWArrayList#iterator()} and {@link COWArrayList#listIterator()} are
043 * <b>not</b> GC-free.
044 * </p>
045 * 
046 * @author Ceki Gulcu
047 * @since 1.1.10
048 */
049public class COWArrayList<E> implements List<E> {
050
051    // Implementation note: markAsStale() should always be invoked *after*
052    // list-modifying actions.
053    // If not, readers might get a stale array until the next write. The potential
054    // problem is nicely
055    // explained by Rob Eden. See
056    // https://github.com/qos-ch/logback/commit/32a2047a1adfc#commitcomment-20791176
057
058    AtomicBoolean fresh = new AtomicBoolean(false);
059    CopyOnWriteArrayList<E> underlyingList = new CopyOnWriteArrayList<E>();
060    E[] ourCopy;
061    final E[] modelArray;
062
063    public COWArrayList(E[] modelArray) {
064        this.modelArray = modelArray;
065    }
066
067    @Override
068    public int size() {
069        return underlyingList.size();
070    }
071
072    @Override
073    public boolean isEmpty() {
074        return underlyingList.isEmpty();
075    }
076
077    @Override
078    public boolean contains(Object o) {
079        return underlyingList.contains(o);
080    }
081
082    @Override
083    public Iterator<E> iterator() {
084        return underlyingList.iterator();
085    }
086
087    private void refreshCopyIfNecessary() {
088        if (!isFresh()) {
089            refreshCopy();
090        }
091    }
092
093    private boolean isFresh() {
094        return fresh.get();
095    }
096
097    private void refreshCopy() {
098        ourCopy = underlyingList.toArray(modelArray);
099        fresh.set(true);
100    }
101
102    @Override
103    public Object[] toArray() {
104        refreshCopyIfNecessary();
105        return ourCopy;
106    }
107
108    @SuppressWarnings("unchecked")
109    @Override
110    public <T> T[] toArray(T[] a) {
111        refreshCopyIfNecessary();
112        return (T[]) ourCopy;
113    }
114
115    /**
116     * Return an array of type E[]. The returned array is intended to be iterated
117     * over. If the list is modified, subsequent calls to this method will return
118     * different/modified array instances.
119     * 
120     * @return
121     */
122    public E[] asTypedArray() {
123        refreshCopyIfNecessary();
124        return ourCopy;
125    }
126
127    private void markAsStale() {
128        fresh.set(false);
129    }
130
131    public void addIfAbsent(E e) {
132        underlyingList.addIfAbsent(e);
133        markAsStale();
134    }
135
136    @Override
137    public boolean add(E e) {
138        boolean result = underlyingList.add(e);
139        markAsStale();
140        return result;
141    }
142
143    @Override
144    public boolean remove(Object o) {
145        boolean result = underlyingList.remove(o);
146        markAsStale();
147        return result;
148    }
149
150    @Override
151    public boolean containsAll(Collection<?> c) {
152        return underlyingList.containsAll(c);
153    }
154
155    @Override
156    public boolean addAll(Collection<? extends E> c) {
157        markAsStale();
158        boolean result = underlyingList.addAll(c);
159        return result;
160    }
161
162    @Override
163    public boolean addAll(int index, Collection<? extends E> col) {
164        markAsStale();
165        boolean result = underlyingList.addAll(index, col);
166        return result;
167    }
168
169    @Override
170    public boolean removeAll(Collection<?> col) {
171        markAsStale();
172        boolean result = underlyingList.removeAll(col);
173        return result;
174    }
175
176    @Override
177    public boolean retainAll(Collection<?> col) {
178        markAsStale();
179        boolean result = underlyingList.retainAll(col);
180        return result;
181    }
182
183    @Override
184    public void clear() {
185        markAsStale();
186        underlyingList.clear();
187    }
188
189    @Override
190    public E get(int index) {
191        refreshCopyIfNecessary();
192        return (E) ourCopy[index];
193    }
194
195    @Override
196    public E set(int index, E element) {
197        markAsStale();
198        E e = underlyingList.set(index, element);
199        return e;
200    }
201
202    @Override
203    public void add(int index, E element) {
204        markAsStale();
205        underlyingList.add(index, element);
206    }
207
208    @Override
209    public E remove(int index) {
210        markAsStale();
211        E e = (E) underlyingList.remove(index);
212        return e;
213    }
214
215    @Override
216    public int indexOf(Object o) {
217        return underlyingList.indexOf(o);
218    }
219
220    @Override
221    public int lastIndexOf(Object o) {
222        return underlyingList.lastIndexOf(o);
223    }
224
225    @Override
226    public ListIterator<E> listIterator() {
227        return underlyingList.listIterator();
228    }
229
230    @Override
231    public ListIterator<E> listIterator(int index) {
232        return underlyingList.listIterator(index);
233    }
234
235    @Override
236    public List<E> subList(int fromIndex, int toIndex) {
237        return underlyingList.subList(fromIndex, toIndex);
238    }
239
240}