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
14package com.ibm.icu.impl.coll;
15
16import java.util.concurrent.atomic.AtomicInteger;
17
18import 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 */
95public 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