KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > runtime > ObjectSpace


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
15  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
16  * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
17  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
18  *
19  * Alternatively, the contents of this file may be used under the terms of
20  * either of the GNU General Public License Version 2 or later (the "GPL"),
21  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
22  * in which case the provisions of the GPL or the LGPL are applicable instead
23  * of those above. If you wish to allow use of your version of this file only
24  * under the terms of either the GPL or the LGPL, and not to allow others to
25  * use your version of this file under the terms of the CPL, indicate your
26  * decision by deleting the provisions above and replace them with the notice
27  * and other provisions required by the GPL or the LGPL. If you do not delete
28  * the provisions above, a recipient may use your version of this file under
29  * the terms of any one of the CPL, the GPL or the LGPL.
30  ***** END LICENSE BLOCK *****/

31 package org.jruby.runtime;
32
33 import org.jruby.RubyModule;
34 import org.jruby.RubyProc;
35 import org.jruby.runtime.builtin.IRubyObject;
36 import org.jruby.util.WeakIdentityHashMap;
37
38 import java.lang.ref.ReferenceQueue JavaDoc;
39 import java.lang.ref.WeakReference JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.HashMap JavaDoc;
42 import java.util.Iterator JavaDoc;
43 import java.util.List JavaDoc;
44 import java.util.Map JavaDoc;
45
46 /**
47  * FIXME: This version is faster than the previous, but both suffer from a
48  * crucial flaw: It is impossible to create an ObjectSpace with an iterator
49  * that doesn't either: a. hold on to objects that might otherwise be collected
50  * or b. have no way to guarantee that a call to hasNext() will be correct or
51  * that a subsequent call to next() will produce an object. For our purposes,
52  * for now, this may be acceptable.
53  */

54 public class ObjectSpace {
55     private ReferenceQueue JavaDoc deadReferences = new ReferenceQueue JavaDoc();
56     private WeakReferenceListNode top;
57
58     private ReferenceQueue JavaDoc deadIdentityReferences = new ReferenceQueue JavaDoc();
59     private final Map identities = new HashMap();
60     private final Map identitiesByObject = new WeakIdentityHashMap();
61
62     private long maxId = 4; // Highest reserved id
63

64     public long idOf(IRubyObject rubyObject) {
65         synchronized (identities) {
66             Long JavaDoc longId = (Long JavaDoc) identitiesByObject.get(rubyObject);
67             if (longId == null) {
68                 longId = createId(rubyObject);
69             }
70             return longId.longValue();
71         }
72     }
73
74     private Long JavaDoc createId(IRubyObject object) {
75         cleanIdentities();
76         maxId += 2; // id must always be even
77
Long JavaDoc longMaxId = new Long JavaDoc(maxId);
78         identities.put(longMaxId, new IdReference(object, maxId, deadIdentityReferences));
79         identitiesByObject.put(object, longMaxId);
80         return longMaxId;
81     }
82     
83     public IRubyObject id2ref(long id) {
84         synchronized (identities) {
85             cleanIdentities();
86             IdReference reference = (IdReference) identities.get(new Long JavaDoc(id));
87             if (reference == null)
88                 return null;
89             return (IRubyObject) reference.get();
90         }
91     }
92
93     private void cleanIdentities() {
94         IdReference ref;
95         while ((ref = (IdReference) deadIdentityReferences.poll()) != null)
96             identities.remove(new Long JavaDoc(ref.id()));
97     }
98
99     private Map finalizers = new HashMap();
100     private Map weakRefs = new HashMap();
101     private List JavaDoc finalizersToRun = new ArrayList JavaDoc();
102     private Thread JavaDoc finalizerThread = new FinalizerThread();
103
104     {
105         finalizerThread.start();
106     }
107
108     private class FinalizerThread extends Thread JavaDoc {
109         public FinalizerThread() {
110             super("Ruby Finalizer Thread");
111             setDaemon(true);
112         }
113         public void run() {
114             while(true) {
115                 try {
116                     synchronized(finalizersToRun) {
117                         while(finalizersToRun.isEmpty()) {
118                             try {
119                                 finalizersToRun.wait();
120                             } catch(InterruptedException JavaDoc e) {
121                             }
122                         }
123                         while(!finalizersToRun.isEmpty()) {
124                             ((Runnable JavaDoc)finalizersToRun.remove(0)).run();
125                         }
126                         finalizersToRun.notify();
127                     }
128                 } catch(Exception JavaDoc e) {
129                     //Swallow, since there's no useful action to take here.
130
}
131             }
132         }
133     }
134
135
136     private class FinalizerEntry implements Runnable JavaDoc {
137         private long id;
138         private RubyProc proc;
139         private IRubyObject fid;
140         public FinalizerEntry(IRubyObject obj, long id, RubyProc proc) {
141             this.id = id;
142             this.proc = proc;
143             this.fid = proc.getRuntime().newFixnum(id);
144             synchronized(ObjectSpace.this) {
145                 FinalizerWeakReferenceListNode node = new FinalizerWeakReferenceListNode(obj,deadReferences,top,this);
146                 Long JavaDoc key = new Long JavaDoc(id);
147                 List JavaDoc refl = (List JavaDoc)weakRefs.get(key);
148                 if(null == refl) {
149                     refl = new ArrayList JavaDoc();
150                     weakRefs.put(key,refl);
151                 }
152                 refl.add(node);
153                 top = node;
154             }
155         }
156
157         public void finalize(FinalizerWeakReferenceListNode obj) {
158             synchronized(ObjectSpace.this) {
159                 List JavaDoc refl = (List JavaDoc)weakRefs.get(new Long JavaDoc(id));
160                 refl.remove(obj);
161                 obj.setRan();
162             }
163             _finalize();
164         }
165
166         public void _finalize() {
167             synchronized(finalizersToRun) {
168                 finalizersToRun.add(this);
169                 finalizersToRun.notifyAll();
170             }
171         }
172
173         public void run() {
174             proc.call(new IRubyObject[]{fid});
175         }
176     }
177
178     public void finishFinalizers() {
179         for(Iterator JavaDoc iter = finalizers.keySet().iterator();iter.hasNext();) {
180             Object JavaDoc key = iter.next();
181             for(Iterator JavaDoc iter2 = ((List JavaDoc)finalizers.get(key)).iterator();iter2.hasNext();) {
182                 ((FinalizerEntry)iter2.next())._finalize();
183             }
184         }
185         synchronized(finalizersToRun) {
186             while(!finalizersToRun.isEmpty()) {
187                 finalizersToRun.notify();
188                 try {
189                     finalizersToRun.wait();
190                 } catch (InterruptedException JavaDoc e) {
191                     Thread.currentThread().interrupt();
192                 }
193             }
194         }
195     }
196
197     public synchronized void addFinalizer(IRubyObject obj, long id, RubyProc proc) {
198         List JavaDoc fins = (List JavaDoc)finalizers.get(new Long JavaDoc(id));
199         if(fins == null) {
200             fins = new ArrayList JavaDoc();
201             finalizers.put(new Long JavaDoc(id),fins);
202         }
203         fins.add(new FinalizerEntry(obj,id,proc));
204     }
205
206     public synchronized void removeFinalizers(long id) {
207         finalizers.remove(new Long JavaDoc(id));
208         List JavaDoc refl = (List JavaDoc)weakRefs.get(new Long JavaDoc(id));
209         if(null != refl) {
210             for(Iterator JavaDoc iter = refl.iterator();iter.hasNext();) {
211                 ((FinalizerWeakReferenceListNode)(iter.next())).setRan();
212             }
213         }
214         weakRefs.remove(new Long JavaDoc(id));
215     }
216
217     public synchronized void add(IRubyObject object) {
218         cleanup();
219         top = new WeakReferenceListNode(object, deadReferences, top);
220     }
221
222     public synchronized Iterator JavaDoc iterator(RubyModule rubyClass) {
223         final List JavaDoc objList = new ArrayList JavaDoc();
224         WeakReferenceListNode current = top;
225         while (current != null) {
226             IRubyObject obj = (IRubyObject)current.get();
227             if (obj != null && obj.isKindOf(rubyClass)) {
228                 objList.add(current);
229             }
230             
231             current = current.next;
232         }
233         
234         return new Iterator JavaDoc() {
235             private Iterator JavaDoc iter = objList.iterator();
236             
237             public boolean hasNext() {
238                 throw new UnsupportedOperationException JavaDoc();
239             }
240
241             public Object JavaDoc next() {
242                 Object JavaDoc obj = null;
243                 while (iter.hasNext()) {
244                     WeakReferenceListNode node = (WeakReferenceListNode)iter.next();
245
246                     if(node instanceof FinalizerWeakReferenceListNode) {
247                         continue;
248                     }
249
250                     obj = node.get();
251                     
252                     if (obj != null) break;
253                 }
254                 return obj;
255             }
256
257             public void remove() {
258                 throw new UnsupportedOperationException JavaDoc();
259             }
260         };
261     }
262
263     private synchronized void cleanup() {
264         WeakReferenceListNode reference;
265         while ((reference = (WeakReferenceListNode)deadReferences.poll()) != null) {
266             reference.remove();
267         }
268     }
269
270     private class WeakReferenceListNode extends WeakReference JavaDoc {
271         public WeakReferenceListNode prev;
272         public WeakReferenceListNode next;
273         public WeakReferenceListNode(Object JavaDoc ref, ReferenceQueue JavaDoc queue, WeakReferenceListNode next) {
274             super(ref, queue);
275
276             this.next = next;
277             if (next != null) {
278                 next.prev = this;
279             }
280         }
281
282         public void remove() {
283             synchronized (ObjectSpace.this) {
284                 if (prev != null) {
285                     prev.next = next;
286                 }
287                 if (next != null) {
288                     next.prev = prev;
289                 }
290             }
291         }
292     }
293
294     private class FinalizerWeakReferenceListNode extends WeakReferenceListNode {
295         private FinalizerEntry entry;
296         private boolean ran = false;
297         public FinalizerWeakReferenceListNode(Object JavaDoc ref, ReferenceQueue JavaDoc queue, WeakReferenceListNode next, FinalizerEntry entry) {
298             super(ref, queue, next);
299             this.entry = entry;
300         }
301         public void remove() {
302             super.remove();
303             if(!ran) {
304                 entry.finalize(this);
305             }
306         }
307         public void setRan() {
308             this.ran = true;
309         }
310     }
311
312     private static class IdReference extends WeakReference JavaDoc {
313         private final long id;
314
315         public IdReference(IRubyObject object, long id, ReferenceQueue JavaDoc queue) {
316             super(object, queue);
317             this.id = id;
318         }
319
320         public long id() {
321             return id;
322         }
323     }
324 }
325
Popular Tags