Home | History | Annotate | Download | only in coll
      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&lt;S&gt; 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 
    102         public Reference(T r) {
    103             ref = r;
    104             if(r != null) {
    105                 r.addRef();
    106             }
    107         }
    108 
    109         @SuppressWarnings("unchecked")
    110         @Override
    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 
    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          */
    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 
    144         public void clear() {
    145             if(ref != null) {
    146                 ref.removeRef();
    147                 ref = null;
    148             }
    149         }
    150 
    151         @Override
    152         protected void finalize() throws Throwable {
    153             super.finalize();
    154             clear();
    155         }
    156     }
    157 
    158     /** Initializes refCount to 0. */
    159     public SharedObject() {}
    160 
    161     /** Initializes refCount to 0. */
    162     @Override
    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      */
    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      */
    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      */
    191     public final int getRefCount() { return refCount.get(); }
    192 
    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