| /* |
| ******************************************************************************* |
| * Copyright (C) 2013-2014, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ******************************************************************************* |
| * SharedObject.java, ported from sharedobject.h/.cpp |
| * |
| * C++ version created on: 2013dec19 |
| * created by: Markus W. Scherer |
| */ |
| |
| package com.ibm.icu.impl.coll; |
| |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import com.ibm.icu.util.ICUCloneNotSupportedException; |
| |
| /** |
| * Base class for shared, reference-counted, auto-deleted objects. |
| * Java subclasses are mutable and must implement clone(). |
| * |
| * <p>In C++, the SharedObject base class is used for both memory and ownership management. |
| * In Java, memory management (deletion after last reference is gone) |
| * is up to the garbage collector, |
| * but the reference counter is still used to see whether the referent is the sole owner. |
| * |
| * <p>Usage: |
| * <pre> |
| * class S extends SharedObject { |
| * public clone() { ... } |
| * } |
| * |
| * // Either use the nest class Reference (which costs an extra allocation), |
| * // or duplicate its code in the class that uses S |
| * // (which duplicates code and is more error-prone). |
| * class U { |
| * // For read-only access, use s.readOnly(). |
| * // For writable access, use S ownedS = s.copyOnWrite(); |
| * private SharedObject.Reference<S> s; |
| * // Returns a writable version of s. |
| * // If there is exactly one owner, then s itself is returned. |
| * // If there are multiple owners, then s is replaced with a clone, |
| * // and that is returned. |
| * private S getOwnedS() { |
| * return s.copyOnWrite(); |
| * } |
| * public U clone() { |
| * ... |
| * c.s = s.clone(); |
| * ... |
| * } |
| * } |
| * |
| * class V { |
| * // For read-only access, use s directly. |
| * // For writable access, use S ownedS = getOwnedS(); |
| * private S s; |
| * // Returns a writable version of s. |
| * // If there is exactly one owner, then s itself is returned. |
| * // If there are multiple owners, then s is replaced with a clone, |
| * // and that is returned. |
| * private S getOwnedS() { |
| * if(s.getRefCount() > 1) { |
| * S ownedS = s.clone(); |
| * s.removeRef(); |
| * s = ownedS; |
| * ownedS.addRef(); |
| * } |
| * return s; |
| * } |
| * public U clone() { |
| * ... |
| * s.addRef(); |
| * ... |
| * } |
| * protected void finalize() { |
| * ... |
| * if(s != null) { |
| * s.removeRef(); |
| * s = null; |
| * } |
| * ... |
| * } |
| * } |
| * </pre> |
| * |
| * Either use only Java memory management, or use addRef()/removeRef(). |
| * Sharing requires reference-counting. |
| * |
| * TODO: Consider making this more widely available inside ICU, |
| * or else adopting a different model. |
| */ |
| public class SharedObject implements Cloneable { |
| /** |
| * Similar to a smart pointer, basically a port of the static methods of C++ SharedObject. |
| */ |
| public static final class Reference<T extends SharedObject> implements Cloneable { |
| private T ref; |
| |
| public Reference(T r) { |
| ref = r; |
| if(r != null) { |
| r.addRef(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Reference<T> clone() { |
| Reference<T> c; |
| try { |
| c = (Reference<T>)super.clone(); |
| } catch (CloneNotSupportedException e) { |
| // Should never happen. |
| throw new ICUCloneNotSupportedException(e); |
| } |
| if(ref != null) { |
| ref.addRef(); |
| } |
| return c; |
| } |
| |
| public T readOnly() { return ref; } |
| |
| /** |
| * Returns a writable version of the reference. |
| * If there is exactly one owner, then the reference itself is returned. |
| * If there are multiple owners, then the reference is replaced with a clone, |
| * and that is returned. |
| */ |
| public T copyOnWrite() { |
| T r = ref; |
| if(r.getRefCount() <= 1) { return r; } |
| @SuppressWarnings("unchecked") |
| T r2 = (T)r.clone(); |
| r.removeRef(); |
| ref = r2; |
| r2.addRef(); |
| return r2; |
| } |
| |
| public void clear() { |
| if(ref != null) { |
| ref.removeRef(); |
| ref = null; |
| } |
| } |
| |
| @Override |
| protected void finalize() throws Throwable { |
| super.finalize(); |
| clear(); |
| } |
| } |
| |
| /** Initializes refCount to 0. */ |
| public SharedObject() {} |
| |
| /** Initializes refCount to 0. */ |
| @Override |
| public SharedObject clone() { |
| SharedObject c; |
| try { |
| c = (SharedObject)super.clone(); |
| } catch (CloneNotSupportedException e) { |
| // Should never happen. |
| throw new ICUCloneNotSupportedException(e); |
| } |
| c.refCount = new AtomicInteger(); |
| return c; |
| } |
| |
| /** |
| * Increments the number of references to this object. Thread-safe. |
| */ |
| public final void addRef() { refCount.incrementAndGet(); } |
| /** |
| * Decrements the number of references to this object, |
| * and auto-deletes "this" if the number becomes 0. Thread-safe. |
| */ |
| public final void removeRef() { |
| // Deletion in Java is up to the garbage collector. |
| refCount.decrementAndGet(); |
| } |
| |
| /** |
| * Returns the reference counter. Uses a memory barrier. |
| */ |
| public final int getRefCount() { return refCount.get(); } |
| |
| public final void deleteIfZeroRefCount() { |
| // Deletion in Java is up to the garbage collector. |
| } |
| |
| private AtomicInteger refCount = new AtomicInteger(); |
| } |