com.ibm.icu.impl.coll.SharedObject Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
*******************************************************************************
* 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().
*
* 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.
*
*
Usage:
*
* 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;
* }
* ...
* }
* }
*
*
* 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 implements Cloneable {
private T ref;
public Reference(T r) {
ref = r;
if(r != null) {
r.addRef();
}
}
@SuppressWarnings("unchecked")
@Override
public Reference clone() {
Reference c;
try {
c = (Reference)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();
}