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<Integer> 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 < 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}