blob: f6004a8bc044121160f19653e2442c8a66a23027 [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.shell;
import com.google.gwt.dev.shell.BrowserChannel.RemoteObjectRef;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Keeps track of references to remote objects. When the objects are no longer
* needed, their ids are returned in {@link #getRefIdsForCleanup()}.
*
* @param <T> subtype of RemoteObjectRef contained in this table
*/
public class RemoteObjectTable<T extends RemoteObjectRef> {
/**
* This maps References to RemoteObjectRefs back to the original refId.
* Because we need the refId of the RemoteObjectRef after it's been
* garbage-collected, this state must be stored externally.
*/
private final Map<Reference<T>, Integer> idFromRemoteObject;
/**
* This accumulates remote objects that are no longer referenced on this side
* of the channel.
*/
private final ReferenceQueue<T> refQueue;
/**
* This map associates a remote object ID with a Reference to the
* RemoteObjectRef that currently represents that id.
*/
private final Map<Integer, Reference<T>> remoteObjectFromId;
/**
* Create a new RemoteObjectTable.
*/
public RemoteObjectTable() {
refQueue = new ReferenceQueue<T>();
remoteObjectFromId = new HashMap<Integer, Reference<T>>();
idFromRemoteObject = new IdentityHashMap<Reference<T>, Integer>();
}
/**
* @return the set of remote object reference IDs that should be freed.
*/
public synchronized Set<Integer> getRefIdsForCleanup() {
// Access to these objects is inherently synchronous
Map<Integer, Reference<T>> objectMap = remoteObjectFromId;
Map<Reference<T>, Integer> refIdMap = idFromRemoteObject;
Set<Integer> toReturn = new HashSet<Integer>();
// Find all refIds associated with previous garbage collection cycles
Reference<? extends RemoteObjectRef> ref;
while ((ref = refQueue.poll()) != null) {
Integer i = refIdMap.remove(ref);
assert i != null;
toReturn.add(i);
}
/*
* Check for liveness. This is necessary because the last reference to a
* RemoteObjectRef could have been cleared and a new reference to that refId
* created before this method has been called.
*/
for (Iterator<Integer> i = toReturn.iterator(); i.hasNext();) {
Integer refId = i.next();
if (objectMap.containsKey(refId)) {
if (objectMap.get(refId).get() != null) {
i.remove();
} else {
objectMap.remove(refId);
}
}
}
return toReturn;
}
/**
* Obtain the RemoteObjectRef that is currently in use to act as a proxy for
* the given remote object ID.
*
* @return the RemoteObjectRef or null if the ID is not currently in use
*/
public synchronized T getRemoteObjectRef(int refId) {
if (remoteObjectFromId.containsKey(refId)) {
Reference<T> ref = remoteObjectFromId.get(refId);
T toReturn = ref.get();
if (toReturn != null) {
return toReturn;
}
}
return null;
}
/**
* Check to see if this ID does not already exist.
*
* @param refId reference ID to check
* @return true if this ID is not currently in use
*/
public synchronized boolean isNewObjectId(int refId) {
return !remoteObjectFromId.containsKey(refId)
|| (remoteObjectFromId.get(refId).get() == null);
}
/**
* Store a remote object reference in the table.
*
* @param refId
* @param remoteObjectRef
*/
public synchronized void putRemoteObjectRef(int refId,
T remoteObjectRef) {
Reference<T> ref = new WeakReference<T>(remoteObjectRef, refQueue);
remoteObjectFromId.put(refId, ref);
idFromRemoteObject.put(ref, refId);
}
}