1 /* 2 * Created on 15-Mar-2006 3 * Created by Paul Gardner 4 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 */ 19 20 package com.aelitis.azureus.core.util; 21 22 import java.lang.ref.WeakReference; 23 import java.util.*; 24 25 import org.gudy.azureus2.core3.util.AEDiagnostics; 26 import org.gudy.azureus2.core3.util.AEDiagnosticsEvidenceGenerator; 27 import org.gudy.azureus2.core3.util.Constants; 28 import org.gudy.azureus2.core3.util.Debug; 29 import org.gudy.azureus2.core3.util.IndentWriter; 30 31 public class 32 CopyOnWriteList<T> 33 implements Iterable<T> 34 { 35 private static final boolean LOG_STATS = false; 36 37 //private int mutation_count = 0; 38 39 private List<T> list = Collections.EMPTY_LIST; 40 41 private final boolean use_linked_list; 42 43 private boolean visible = false; 44 45 private int initialCapacity; 46 47 private static CopyOnWriteList stats; 48 49 private int mutation_count; 50 51 static { 52 if (LOG_STATS) { 53 stats = new CopyOnWriteList(10); AEDiagnostics.addEvidenceGenerator(new AEDiagnosticsEvidenceGenerator() { public void generate(IndentWriter writer) { writer.println(R); writer.indent(); try { long count = 0; long size = 0; for (Iterator iter = stats.iterator(); iter.hasNext();) { WeakReference wf = (WeakReference) iter.next(); CopyOnWriteList cowList = (CopyOnWriteList) wf.get(); if (cowList != null) { count++; size += cowList.size(); } } writer.println(count + R + size + R); if ( count > 0 ){ writer.println((size/count) + R); } } catch (Throwable t) { } finally { writer.exdent(); } } })54 AEDiagnostics.addEvidenceGenerator(new AEDiagnosticsEvidenceGenerator() { 55 public void generate(IndentWriter writer) { 56 writer.println("COWList Info"); 57 writer.indent(); 58 try { 59 long count = 0; 60 long size = 0; 61 for (Iterator iter = stats.iterator(); iter.hasNext();) { 62 WeakReference wf = (WeakReference) iter.next(); 63 CopyOnWriteList cowList = (CopyOnWriteList) wf.get(); 64 if (cowList != null) { 65 count++; 66 size += cowList.size(); 67 } 68 } 69 writer.println(count + " lists with " + size + " total entries"); 70 if ( count > 0 ){ 71 writer.println((size/count) + " avg size"); 72 } 73 } catch (Throwable t) { 74 } finally { 75 writer.exdent(); 76 } 77 } 78 }); 79 } 80 } 81 82 /** 83 * @param i 84 */ CopyOnWriteList(int initialCapacity)85 public CopyOnWriteList(int initialCapacity) { 86 this.initialCapacity = initialCapacity; 87 use_linked_list = false; 88 if (LOG_STATS) { 89 stats.add(new WeakReference(this)); 90 } 91 } 92 93 /** 94 * 95 */ CopyOnWriteList()96 public CopyOnWriteList() { 97 // Smaller default initial capacity as most of our lists are small 98 // Last check on 7/24/2008: 444 lists with 456 total entries 99 this.initialCapacity = 1; 100 use_linked_list = false; 101 if (LOG_STATS) { 102 stats.add(new WeakReference(this)); 103 } 104 } 105 106 public CopyOnWriteList(boolean _use_linked_list )107 CopyOnWriteList(boolean _use_linked_list ) { 108 this.initialCapacity = 1; 109 use_linked_list = _use_linked_list; 110 if (LOG_STATS) { 111 stats.add(new WeakReference(this)); 112 } 113 } 114 115 public int getMutationCount()116 getMutationCount() 117 { 118 synchronized( this ){ 119 120 return( mutation_count ); 121 } 122 } 123 124 public void add( T obj )125 add( 126 T obj ) 127 { 128 synchronized( this ){ 129 130 mutation_count++; 131 132 if ( visible ){ 133 134 List<T> new_list = use_linked_list?new LinkedList<T>( list ):new ArrayList<T>( list ); 135 136 //mutated(); 137 138 new_list.add( obj ); 139 140 list = new_list; 141 142 visible = false; 143 144 }else{ 145 if (list == Collections.EMPTY_LIST) { 146 list = use_linked_list?new LinkedList<T>():new ArrayList<T>(initialCapacity); 147 } 148 149 list.add( obj ); 150 } 151 } 152 } 153 154 /** 155 * 156 * @param obj 157 * @return true if added, false if not 158 */ 159 160 public boolean addIfNotPresent( T obj )161 addIfNotPresent( 162 T obj ) 163 { 164 synchronized( this ){ 165 166 if ( list.contains( obj )){ 167 168 return(false ); 169 }else{ 170 171 add( obj ); 172 173 return( true ); 174 } 175 } 176 } 177 178 public void add( int index, T obj )179 add( 180 int index, 181 T obj ) 182 { 183 if ( Constants.IS_CVS_VERSION && use_linked_list ){ 184 Debug.out( "hmm" ); 185 } 186 synchronized( this ){ 187 188 mutation_count++; 189 190 if ( visible ){ 191 192 List<T> new_list = use_linked_list?new LinkedList<T>(list):new ArrayList<T>( list ); 193 194 //mutated(); 195 196 new_list.add( index, obj ); 197 198 list = new_list; 199 200 visible = false; 201 202 }else{ 203 if (list == Collections.EMPTY_LIST) { 204 list = use_linked_list?new LinkedList<T>():new ArrayList<T>(initialCapacity); 205 } 206 207 list.add( index, obj ); 208 } 209 } 210 } 211 212 public void addAll( Collection<T> c )213 addAll( 214 Collection<T> c ) 215 { 216 synchronized( this ){ 217 218 mutation_count++; 219 220 if ( visible ){ 221 222 List<T> new_list = use_linked_list?new LinkedList<T>( list ):new ArrayList<T>( list ); 223 224 //mutated(); 225 226 new_list.addAll( c ); 227 228 list = new_list; 229 230 visible = false; 231 232 }else{ 233 if (list == Collections.EMPTY_LIST) { 234 list = use_linked_list?new LinkedList<T>():new ArrayList<T>(initialCapacity); 235 } 236 237 list.addAll( c ); 238 } 239 } 240 } 241 242 public T get( int index )243 get( 244 int index ) 245 { 246 if ( Constants.IS_CVS_VERSION && use_linked_list ){ 247 Debug.out( "hmm" ); 248 } 249 250 synchronized( this ){ 251 252 return( list.get(index)); 253 } 254 } 255 256 public boolean remove( T obj )257 remove( 258 T obj ) 259 { 260 synchronized( this ){ 261 262 mutation_count++; 263 264 if ( visible ){ 265 266 List<T> new_list = use_linked_list?new LinkedList<T>(list):new ArrayList<T>( list ); 267 268 //mutated(); 269 270 boolean result = new_list.remove( obj ); 271 272 list = new_list; 273 274 visible = false; 275 276 return( result ); 277 278 }else{ 279 280 return( list.remove( obj )); 281 } 282 } 283 } 284 285 public void clear()286 clear() 287 { 288 synchronized( this ){ 289 290 mutation_count++; 291 292 list = Collections.EMPTY_LIST; 293 294 visible = false; 295 } 296 } 297 298 public boolean contains( T obj )299 contains( 300 T obj ) 301 { 302 synchronized( this ){ 303 304 return( list.contains( obj )); 305 } 306 } 307 308 public Iterator<T> iterator()309 iterator() 310 { 311 synchronized( this ){ 312 313 visible = true; 314 315 return( new CopyOnWriteListIterator( list.iterator())); 316 } 317 } 318 319 public List<T> getList()320 getList() 321 { 322 // TODO: we need to either make this a read-only-list or obey the copy-on-write semantics correctly... 323 324 synchronized( this ){ 325 326 visible = true; 327 328 if ( Constants.IS_CVS_VERSION ){ 329 330 return( Collections.unmodifiableList( list )); 331 332 }else{ 333 334 return( list ); 335 } 336 } 337 } 338 339 public int size()340 size() 341 { 342 synchronized( this ){ 343 344 return( list.size()); 345 } 346 } 347 348 public boolean isEmpty()349 isEmpty() 350 { 351 synchronized( this ){ 352 353 return list.isEmpty(); 354 } 355 } 356 357 public Object[] toArray()358 toArray() 359 { 360 synchronized( this ){ 361 362 return( list.toArray()); 363 } 364 } 365 366 public T[] toArray( T[] x )367 toArray( 368 T[] x ) 369 { 370 synchronized( this ){ 371 372 return( list.toArray(x)); 373 } 374 } 375 376 /* 377 private void 378 mutated() 379 { 380 mutation_count++; 381 382 if ( mutation_count%10 == 0 ){ 383 384 System.out.println( this + ": mut=" + mutation_count ); 385 } 386 } 387 */ 388 389 private class 390 CopyOnWriteListIterator 391 implements Iterator<T> 392 { 393 private Iterator<T> it; 394 private T last; 395 396 protected CopyOnWriteListIterator( Iterator<T> _it )397 CopyOnWriteListIterator( 398 Iterator<T> _it ) 399 { 400 it = _it; 401 } 402 403 public boolean hasNext()404 hasNext() 405 { 406 return( it.hasNext()); 407 } 408 409 public T next()410 next() 411 { 412 last = it.next(); 413 414 return( last ); 415 } 416 417 public void remove()418 remove() 419 { 420 // don't actually remove it from the iterator. can't go backwards with this iterator so this is 421 // not a problem 422 423 if ( last == null ){ 424 425 throw( new IllegalStateException( "next has not been called!" )); 426 } 427 428 CopyOnWriteList.this.remove( last ); 429 } 430 } 431 getInitialCapacity()432 public int getInitialCapacity() { 433 return initialCapacity; 434 } 435 setInitialCapacity(int initialCapacity)436 public void setInitialCapacity(int initialCapacity) { 437 this.initialCapacity = initialCapacity; 438 } 439 } 440