001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.mm.mmtk;
014    
015    import org.vmmagic.pragma.*;
016    import org.vmmagic.unboxed.*;
017    
018    import org.jikesrvm.SizeConstants;
019    import org.jikesrvm.VM;
020    import org.jikesrvm.mm.mminterface.Selected;
021    import org.jikesrvm.runtime.Magic;
022    import org.jikesrvm.Services;
023    import org.mmtk.plan.TraceLocal;
024    
025    /**
026     * This class manages the processing of finalizable objects.
027     */
028    // can this be a linked list?
029    @Uninterruptible
030    public final class FinalizableProcessor extends org.mmtk.vm.FinalizableProcessor implements SizeConstants {
031    
032      /********************************************************************
033       * Class fields
034       */
035    
036      /** The FinalizableProcessor singleton */
037      private static final FinalizableProcessor finalizableProcessor = new FinalizableProcessor();
038    
039      /** Stress the system? */
040      private static final boolean STRESS = VM.ForceFrequentGC;
041    
042      /** Initial size of the reference object table */
043      private static final int INITIAL_SIZE = STRESS ? 1 : 256;
044    
045      /** Amount to grow the table by when it is filled */
046      private static final double GROWTH_FACTOR = 2.0;
047    
048      /*************************************************************************
049       * Instance fields
050       */
051    
052      /** Used to ensure mutual exclusion during table manipulation */
053      private final Lock lock = new Lock("AddressTable");
054    
055      /** The table of candidates */
056      protected volatile AddressArray table = AddressArray.create(INITIAL_SIZE);
057    
058      /** The table of ready objects */
059      protected volatile Object[] readyForFinalize = new Object[INITIAL_SIZE];
060    
061      /** Index of first entry created since last collection */
062      protected int nurseryIndex = 0;
063    
064      /** Index of the first free slot in the table. */
065      protected volatile int maxIndex = 0;
066    
067      /** Next object ready to be finalized */
068      private volatile int nextReadyIndex = 0;
069    
070      /** Last object ready to be finalized */
071      private volatile int lastReadyIndex = 0;
072    
073      /**
074       * Create a new table.
075       */
076      protected FinalizableProcessor() {}
077    
078      /**
079       * Allocate an entry in the table. This should be called from an unpreemptible
080       * context so that the entry can be filled. This method is responsible for growing
081       * the table if necessary.
082       */
083      @NoInline
084      @UnpreemptibleNoWarn("Non-preemptible but yield when table needs to be grown")
085      public void add(Object object) {
086        lock.acquire();
087        while (maxIndex>=table.length() || maxIndex >= freeReady()) {
088          int newTableSize=-1;
089          int newReadyForFinalizeSize=-1;
090          AddressArray newTable=null;
091          Object[] newReadyForFinalize=null;
092    
093          if (maxIndex>=table.length()) {
094            newTableSize=STRESS ? table.length() + 1 : (int)(table.length() * GROWTH_FACTOR);
095          }
096    
097          if (maxIndex>=freeReady()) {
098            newReadyForFinalizeSize=table.length() + countReady();
099            if (newReadyForFinalizeSize<=readyForFinalize.length) {
100              newReadyForFinalizeSize=-1;
101            }
102          }
103    
104          {
105            lock.release();
106            if (newTableSize>=0) {
107              newTable=AddressArray.create(newTableSize);
108            }
109            if (newReadyForFinalizeSize>=0) {
110              newReadyForFinalize=new Object[newReadyForFinalizeSize];
111            }
112            lock.acquire();
113          }
114    
115          if (maxIndex>=table.length() && newTable!=null) {
116            for (int i=0; i < table.length(); i++) {
117              newTable.set(i, table.get(i));
118            }
119            table = newTable;
120          }
121    
122          if (maxIndex>=freeReady() && newReadyForFinalize!=null) {
123            int j = 0;
124            for(int i=nextReadyIndex; i < lastReadyIndex && i < readyForFinalize.length; i++) {
125              newReadyForFinalize[j++] = readyForFinalize[i];
126            }
127            if (lastReadyIndex < nextReadyIndex) {
128              for(int i=0; i < lastReadyIndex; i++) {
129                newReadyForFinalize[j++] = readyForFinalize[i];
130              }
131            }
132            lastReadyIndex = j;
133            nextReadyIndex = 0;
134            readyForFinalize = newReadyForFinalize;
135          }
136        }
137        table.set(maxIndex++, Magic.objectAsAddress(object));
138        lock.release();
139      }
140    
141      /**
142       * Clear the contents of the table. This is called when reference types are
143       * disabled to make it easier for VMs to change this setting at runtime.
144       */
145      public void clear() {
146        maxIndex = 0;
147      }
148    
149      /**
150       * Scan through all entries in the table and forward.
151       *
152       * Currently ignores the nursery hint.
153       *
154       * TODO parallelise this code?
155       *
156       * @param trace The trace
157       * @param nursery Is this a nursery collection ?
158       */
159      @Override
160      public void forward(TraceLocal trace, boolean nursery) {
161        for (int i=0 ; i < maxIndex; i++) {
162          ObjectReference ref = table.get(i).toObjectReference();
163          table.set(i, trace.getForwardedFinalizable(ref).toAddress());
164        }
165      }
166    
167      /**
168       * Scan through the list of references. Calls ReferenceProcessor's
169       * processReference method for each reference and builds a new
170       * list of those references still active.
171       *
172       * Depending on the value of <code>nursery</code>, we will either
173       * scan all references, or just those created since the last scan.
174       *
175       * TODO parallelise this code
176       *
177       * @param nursery Scan only the newly created references
178       */
179      @Override
180      @UninterruptibleNoWarn
181      public void scan(TraceLocal trace, boolean nursery) {
182        int toIndex = nursery ? nurseryIndex : 0;
183    
184        for (int fromIndex = toIndex; fromIndex < maxIndex; fromIndex++) {
185          ObjectReference ref = table.get(fromIndex).toObjectReference();
186    
187          /* Determine liveness (and forward if necessary) */
188          if (trace.isLive(ref)) {
189            table.set(toIndex++, trace.getForwardedFinalizable(ref).toAddress());
190            continue;
191          }
192    
193          /* Make ready for finalize */
194          ref = trace.retainForFinalize(ref);
195    
196          /* Add to object table */
197          Offset offset = Word.fromIntZeroExtend(lastReadyIndex).lsh(LOG_BYTES_IN_ADDRESS).toOffset();
198          Selected.Plan.get().storeObjectReference(Magic.objectAsAddress(readyForFinalize).plus(offset), ref);
199          lastReadyIndex = (lastReadyIndex + 1) % readyForFinalize.length;
200        }
201        nurseryIndex = maxIndex = toIndex;
202      }
203    
204      /**
205       * Get an object to run finalize().
206       *
207       * @return The object to finalize()
208       */
209      @NoInline
210      @Unpreemptible("Non-preemptible but may pause if another thread is growing the table")
211      public Object getReady() {
212        lock.acquire();
213        Object result = null;
214        if (nextReadyIndex != lastReadyIndex) {
215          result = readyForFinalize[nextReadyIndex];
216          Services.setArrayUninterruptible(readyForFinalize, nextReadyIndex, null);
217          nextReadyIndex = (nextReadyIndex + 1) % readyForFinalize.length;
218        }
219        lock.release();
220        return result;
221      }
222    
223      /***********************************************************************
224       * Statistics and debugging
225       */
226    
227      /**
228       * The number of entries in the table.
229       */
230      public int count() {
231        return maxIndex;
232      }
233    
234      /**
235       * The number of entries ready to be finalized.
236       */
237      public int countReady() {
238        return ((lastReadyIndex - nextReadyIndex) + readyForFinalize.length) % readyForFinalize.length;
239      }
240    
241      /**
242       * The number of entries ready to be finalized.
243       */
244      public int freeReady() {
245        return readyForFinalize.length - countReady();
246      }
247    
248      /***********************************************************************
249       * Static methods.
250       */
251    
252      /** Get the singleton */
253      public static FinalizableProcessor getProcessor() {
254        return finalizableProcessor;
255      }
256    
257      /**
258       * Add a finalization candidate.
259       * @param object The object with a finalizer.
260       */
261      @Unpreemptible("Non-preemptible but may pause if table needs to be grown")
262      public static void addCandidate(Object object) {
263        finalizableProcessor.add(object);
264      }
265    
266      /**
267       * Get an object to call the finalize() method on it.
268       */
269      @Unpreemptible("Non-preemptible but may pause if table is being grown")
270      public static Object getForFinalize() {
271        return finalizableProcessor.getReady();
272      }
273    
274      /**
275       * The number of objects waiting for finalize() calls.
276       */
277      public static int countReadyForFinalize() {
278        return finalizableProcessor.countReady();
279      }
280    }