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