001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
015: * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
016: * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
017: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
018: *
019: * Alternatively, the contents of this file may be used under the terms of
020: * either of the GNU General Public License Version 2 or later (the "GPL"),
021: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
022: * in which case the provisions of the GPL or the LGPL are applicable instead
023: * of those above. If you wish to allow use of your version of this file only
024: * under the terms of either the GPL or the LGPL, and not to allow others to
025: * use your version of this file under the terms of the CPL, indicate your
026: * decision by deleting the provisions above and replace them with the notice
027: * and other provisions required by the GPL or the LGPL. If you do not delete
028: * the provisions above, a recipient may use your version of this file under
029: * the terms of any one of the CPL, the GPL or the LGPL.
030: ***** END LICENSE BLOCK *****/package org.jruby.runtime;
031:
032: import org.jruby.RubyModule;
033: import org.jruby.RubyProc;
034: import org.jruby.runtime.builtin.IRubyObject;
035: import org.jruby.util.WeakIdentityHashMap;
036:
037: import java.lang.ref.ReferenceQueue;
038: import java.lang.ref.WeakReference;
039: import java.util.ArrayList;
040: import java.util.HashMap;
041: import java.util.Iterator;
042: import java.util.List;
043: import java.util.Map;
044:
045: /**
046: * FIXME: This version is faster than the previous, but both suffer from a
047: * crucial flaw: It is impossible to create an ObjectSpace with an iterator
048: * that doesn't either: a. hold on to objects that might otherwise be collected
049: * or b. have no way to guarantee that a call to hasNext() will be correct or
050: * that a subsequent call to next() will produce an object. For our purposes,
051: * for now, this may be acceptable.
052: */
053: public class ObjectSpace {
054: private ReferenceQueue deadReferences = new ReferenceQueue();
055: private WeakReferenceListNode top;
056:
057: private ReferenceQueue deadIdentityReferences = new ReferenceQueue();
058: private final Map identities = new HashMap();
059: private final Map identitiesByObject = new WeakIdentityHashMap();
060:
061: private long maxId = 4; // Highest reserved id
062:
063: public long idOf(IRubyObject rubyObject) {
064: synchronized (identities) {
065: Long longId = (Long) identitiesByObject.get(rubyObject);
066: if (longId == null) {
067: longId = createId(rubyObject);
068: }
069: return longId.longValue();
070: }
071: }
072:
073: private Long createId(IRubyObject object) {
074: cleanIdentities();
075: maxId += 2; // id must always be even
076: Long longMaxId = new Long(maxId);
077: identities.put(longMaxId, new IdReference(object, maxId,
078: deadIdentityReferences));
079: identitiesByObject.put(object, longMaxId);
080: return longMaxId;
081: }
082:
083: public IRubyObject id2ref(long id) {
084: synchronized (identities) {
085: cleanIdentities();
086: IdReference reference = (IdReference) identities
087: .get(new Long(id));
088: if (reference == null)
089: return null;
090: return (IRubyObject) reference.get();
091: }
092: }
093:
094: private void cleanIdentities() {
095: IdReference ref;
096: while ((ref = (IdReference) deadIdentityReferences.poll()) != null)
097: identities.remove(new Long(ref.id()));
098: }
099:
100: public void addFinalizer(IRubyObject object, RubyProc proc) {
101: object.addFinalizer(proc);
102: }
103:
104: public void removeFinalizers(long id) {
105: IRubyObject object = id2ref(id);
106: if (object != null) {
107: object.removeFinalizers();
108: }
109: }
110:
111: public synchronized void add(IRubyObject object) {
112: cleanup();
113: top = new WeakReferenceListNode(object, deadReferences, top);
114: }
115:
116: public synchronized Iterator iterator(RubyModule rubyClass) {
117: final List objList = new ArrayList();
118: WeakReferenceListNode current = top;
119: while (current != null) {
120: IRubyObject obj = (IRubyObject) current.get();
121: if (obj != null && obj.isKindOf(rubyClass)) {
122: objList.add(current);
123: }
124:
125: current = current.nextNode;
126: }
127:
128: return new Iterator() {
129: private Iterator iter = objList.iterator();
130:
131: public boolean hasNext() {
132: throw new UnsupportedOperationException();
133: }
134:
135: public Object next() {
136: Object obj = null;
137: while (iter.hasNext()) {
138: WeakReferenceListNode node = (WeakReferenceListNode) iter
139: .next();
140:
141: obj = node.get();
142:
143: if (obj != null)
144: break;
145: }
146: return obj;
147: }
148:
149: public void remove() {
150: throw new UnsupportedOperationException();
151: }
152: };
153: }
154:
155: private synchronized void cleanup() {
156: WeakReferenceListNode reference;
157: while ((reference = (WeakReferenceListNode) deadReferences
158: .poll()) != null) {
159: reference.remove();
160: }
161: }
162:
163: private class WeakReferenceListNode extends WeakReference {
164: private WeakReferenceListNode prevNode;
165: private WeakReferenceListNode nextNode;
166:
167: public WeakReferenceListNode(Object ref, ReferenceQueue queue,
168: WeakReferenceListNode next) {
169: super (ref, queue);
170:
171: this .nextNode = next;
172: if (next != null) {
173: next.prevNode = this ;
174: }
175: }
176:
177: public void remove() {
178: synchronized (ObjectSpace.this ) {
179: if (prevNode != null) {
180: prevNode.nextNode = nextNode;
181: } else {
182: top = nextNode;
183: }
184: if (nextNode != null) {
185: nextNode.prevNode = prevNode;
186: }
187: }
188: }
189: }
190:
191: private static class IdReference extends WeakReference {
192: private final long id;
193:
194: public IdReference(IRubyObject object, long id,
195: ReferenceQueue queue) {
196: super (object, queue);
197: this .id = id;
198: }
199:
200: public long id() {
201: return id;
202: }
203: }
204: }
|