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