1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2013-2014, International Business Machines 6 * Corporation and others. All Rights Reserved. 7 ******************************************************************************* 8 * SharedObject.java, ported from sharedobject.h/.cpp 9 * 10 * C++ version created on: 2013dec19 11 * created by: Markus W. Scherer 12 */ 13 14 package com.ibm.icu.impl.coll; 15 16 import java.util.concurrent.atomic.AtomicInteger; 17 18 import com.ibm.icu.util.ICUCloneNotSupportedException; 19 20 /** 21 * Base class for shared, reference-counted, auto-deleted objects. 22 * Java subclasses are mutable and must implement clone(). 23 * 24 * <p>In C++, the SharedObject base class is used for both memory and ownership management. 25 * In Java, memory management (deletion after last reference is gone) 26 * is up to the garbage collector, 27 * but the reference counter is still used to see whether the referent is the sole owner. 28 * 29 * <p>Usage: 30 * <pre> 31 * class S extends SharedObject { 32 * public clone() { ... } 33 * } 34 * 35 * // Either use the nest class Reference (which costs an extra allocation), 36 * // or duplicate its code in the class that uses S 37 * // (which duplicates code and is more error-prone). 38 * class U { 39 * // For read-only access, use s.readOnly(). 40 * // For writable access, use S ownedS = s.copyOnWrite(); 41 * private SharedObject.Reference<S> s; 42 * // Returns a writable version of s. 43 * // If there is exactly one owner, then s itself is returned. 44 * // If there are multiple owners, then s is replaced with a clone, 45 * // and that is returned. 46 * private S getOwnedS() { 47 * return s.copyOnWrite(); 48 * } 49 * public U clone() { 50 * ... 51 * c.s = s.clone(); 52 * ... 53 * } 54 * } 55 * 56 * class V { 57 * // For read-only access, use s directly. 58 * // For writable access, use S ownedS = getOwnedS(); 59 * private S s; 60 * // Returns a writable version of s. 61 * // If there is exactly one owner, then s itself is returned. 62 * // If there are multiple owners, then s is replaced with a clone, 63 * // and that is returned. 64 * private S getOwnedS() { 65 * if(s.getRefCount() > 1) { 66 * S ownedS = s.clone(); 67 * s.removeRef(); 68 * s = ownedS; 69 * ownedS.addRef(); 70 * } 71 * return s; 72 * } 73 * public U clone() { 74 * ... 75 * s.addRef(); 76 * ... 77 * } 78 * protected void finalize() { 79 * ... 80 * if(s != null) { 81 * s.removeRef(); 82 * s = null; 83 * } 84 * ... 85 * } 86 * } 87 * </pre> 88 * 89 * Either use only Java memory management, or use addRef()/removeRef(). 90 * Sharing requires reference-counting. 91 * 92 * TODO: Consider making this more widely available inside ICU, 93 * or else adopting a different model. 94 */ 95 public class SharedObject implements Cloneable { 96 /** 97 * Similar to a smart pointer, basically a port of the static methods of C++ SharedObject. 98 */ 99 public static final class Reference<T extends SharedObject> implements Cloneable { 100 private T ref; 101 Reference(T r)102 public Reference(T r) { 103 ref = r; 104 if(r != null) { 105 r.addRef(); 106 } 107 } 108 109 @SuppressWarnings("unchecked") 110 @Override clone()111 public Reference<T> clone() { 112 Reference<T> c; 113 try { 114 c = (Reference<T>)super.clone(); 115 } catch (CloneNotSupportedException e) { 116 // Should never happen. 117 throw new ICUCloneNotSupportedException(e); 118 } 119 if(ref != null) { 120 ref.addRef(); 121 } 122 return c; 123 } 124 readOnly()125 public T readOnly() { return ref; } 126 127 /** 128 * Returns a writable version of the reference. 129 * If there is exactly one owner, then the reference itself is returned. 130 * If there are multiple owners, then the reference is replaced with a clone, 131 * and that is returned. 132 */ copyOnWrite()133 public T copyOnWrite() { 134 T r = ref; 135 if(r.getRefCount() <= 1) { return r; } 136 @SuppressWarnings("unchecked") 137 T r2 = (T)r.clone(); 138 r.removeRef(); 139 ref = r2; 140 r2.addRef(); 141 return r2; 142 } 143 clear()144 public void clear() { 145 if(ref != null) { 146 ref.removeRef(); 147 ref = null; 148 } 149 } 150 151 @Override finalize()152 protected void finalize() throws Throwable { 153 super.finalize(); 154 clear(); 155 } 156 } 157 158 /** Initializes refCount to 0. */ SharedObject()159 public SharedObject() {} 160 161 /** Initializes refCount to 0. */ 162 @Override clone()163 public SharedObject clone() { 164 SharedObject c; 165 try { 166 c = (SharedObject)super.clone(); 167 } catch (CloneNotSupportedException e) { 168 // Should never happen. 169 throw new ICUCloneNotSupportedException(e); 170 } 171 c.refCount = new AtomicInteger(); 172 return c; 173 } 174 175 /** 176 * Increments the number of references to this object. Thread-safe. 177 */ addRef()178 public final void addRef() { refCount.incrementAndGet(); } 179 /** 180 * Decrements the number of references to this object, 181 * and auto-deletes "this" if the number becomes 0. Thread-safe. 182 */ removeRef()183 public final void removeRef() { 184 // Deletion in Java is up to the garbage collector. 185 refCount.decrementAndGet(); 186 } 187 188 /** 189 * Returns the reference counter. Uses a memory barrier. 190 */ getRefCount()191 public final int getRefCount() { return refCount.get(); } 192 deleteIfZeroRefCount()193 public final void deleteIfZeroRefCount() { 194 // Deletion in Java is up to the garbage collector. 195 } 196 197 private AtomicInteger refCount = new AtomicInteger(); 198 } 199