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