| /* |
| * 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; |
| } |
| |
| /** |
| * 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); |
| } |
| } |