1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.core.util;
16  
17  import java.util.Collection;
18  import java.util.Iterator;
19  import java.util.List;
20  import java.util.ListIterator;
21  import java.util.concurrent.CopyOnWriteArrayList;
22  import java.util.concurrent.atomic.AtomicBoolean;
23  
24  /**
25   * A GC-free lock-free thread-safe implementation of the {@link List} interface
26   * for use cases where iterations over the list vastly out-number modifications
27   * on the list.
28   * 
29   * <p>
30   * Underneath, it wraps an instance of {@link CopyOnWriteArrayList} and exposes
31   * a copy of the array used by that instance.
32   * 
33   * <p>
34   * Typical use:
35   * </p>
36   * 
37   * <pre>
38   *   COWArrayList&lt;Integer&gt; list = new COWArrayList(new Integer[0]);
39   *   
40   *   // modify the list
41   *   list.add(1);
42   *   list.add(2);
43   *   
44   *   Integer[] intArray = list.asTypedArray();
45   *   int sum = 0;
46   *   // iteration over the array is thread-safe
47   *   for(int i = 0; i &lt; intArray.length; i++) {
48   *     sum != intArray[i];
49   *   }
50   * </pre>
51   * 
52   * <p>
53   * If the list is not modified, then repetitive calls to
54   * {@link #asTypedArray()}, {@link #toArray()} and {@link #toArray(Object[])}
55   * are guaranteed to be GC-free. Note that iterating over the list using
56   * {@link COWArrayList#iterator()} and {@link COWArrayList#listIterator()} are
57   * <b>not</b> GC-free.
58   * </p>
59   * 
60   * @author Ceki Gulcu
61   * @since 1.1.10
62   */
63  public class COWArrayList<E> implements List<E> {
64  
65      // Implementation note: markAsStale() should always be invoked *after*
66      // list-modifying actions.
67      // If not, readers might get a stale array until the next write. The potential
68      // problem is nicely
69      // explained by Rob Eden. See
70      // https://github.com/qos-ch/logback/commit/32a2047a1adfc#commitcomment-20791176
71  
72      AtomicBoolean fresh = new AtomicBoolean(false);
73      CopyOnWriteArrayList<E> underlyingList = new CopyOnWriteArrayList<E>();
74      E[] ourCopy;
75      final E[] modelArray;
76  
77      public COWArrayList(E[] modelArray) {
78          this.modelArray = modelArray;
79      }
80  
81      @Override
82      public int size() {
83          return underlyingList.size();
84      }
85  
86      @Override
87      public boolean isEmpty() {
88          return underlyingList.isEmpty();
89      }
90  
91      @Override
92      public boolean contains(Object o) {
93          return underlyingList.contains(o);
94      }
95  
96      @Override
97      public Iterator<E> iterator() {
98          return underlyingList.iterator();
99      }
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 }