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