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