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