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.mmtk.utility.heap;
014    
015    import org.mmtk.plan.Plan;
016    import org.mmtk.policy.Space;
017    import org.mmtk.utility.Constants;
018    import org.mmtk.utility.options.ProtectOnRelease;
019    import org.mmtk.utility.options.Options;
020    
021    import org.mmtk.vm.Lock;
022    import org.mmtk.vm.VM;
023    
024    import org.vmmagic.pragma.*;
025    import org.vmmagic.unboxed.*;
026    
027    /**
028     * This class manages the allocation of pages for a space.  When a
029     * page is requested by the space both a page budget and the use of
030     * virtual address space are checked.  If the request for space can't
031     * be satisfied (for either reason) a GC may be triggered.<p>
032     *
033     * This class is abstract, and is subclassed with monotone and
034     * freelist variants, which reflect monotonic and ad hoc space usage
035     * respectively.  Monotonic use is easier to manage, but is obviously
036     * more restrictive (useful for copying collectors which allocate
037     * monotonically before freeing the entire space and starting over).
038     */
039    @Uninterruptible
040    public abstract class PageResource implements Constants {
041    
042      /****************************************************************************
043       *
044       * Class variables
045       */
046      protected static final boolean ZERO_ON_RELEASE = false; // debugging
047    
048      private static final Lock classLock;
049      private static long cumulativeCommitted = 0;
050    
051    
052      /****************************************************************************
053       *
054       * Instance variables
055       */
056    
057      // page budgeting
058      protected int reserved;
059      protected int committed;
060      protected int required;
061      private final int pageBudget;
062    
063      protected final boolean contiguous;
064      protected final Space space;
065      protected Address start; // only for contiguous
066    
067      // locking
068      private final Lock gcLock; // used during GC
069      private final Lock mutatorLock; // used by mutators
070    
071      /****************************************************************************
072       *
073       * Initialization
074       */
075      static {
076        classLock = VM.newLock("PageResource");
077        Options.protectOnRelease = new ProtectOnRelease();
078      }
079    
080      /**
081       * Constructor
082       *
083       * @param pageBudget The budget of pages available to this memory
084       * manager before it must poll the collector.
085       * @param space The space to which this resource is attached
086       */
087      private PageResource(int pageBudget, Space space, boolean contiguous) {
088        this.pageBudget = pageBudget;
089        this.contiguous = contiguous;
090        this.space = space;
091        gcLock = VM.newLock(space.getName() + ".gcLock");
092        mutatorLock = VM.newLock(space.getName() + ".mutatorLock");
093      }
094    
095      /**
096       * Constructor for discontiguous spaces
097       *
098       * @param pageBudget The budget of pages available to this memory
099       * manager before it must poll the collector.
100       * @param space The space to which this resource is attached
101       */
102      PageResource(int pageBudget, Space space) {
103        this(pageBudget, space, false);
104      }
105    
106      /**
107       * Constructor for contiguous spaces
108       *
109       * @param pageBudget The budget of pages available to this memory
110       * manager before it must poll the collector.
111       * @param space The space to which this resource is attached
112       */
113      PageResource(int pageBudget, Space space, Address start) {
114        this(pageBudget, space, true);
115        this.start = start;
116      }
117    
118      /**
119       * Return the number of available physical pages for this resource.
120       *
121       * Note: This just considers physical pages (ie virtual memory pages
122       * allocated for use by this resource). This calculation is orthogonal
123       * to and does not consider any restrictions on the number of pages
124       * this resource may actually use at any time (ie the number of
125       * committed and reserved pages).<p>
126       *
127       * Note: The calculation is made on the assumption that all space that
128       * could be assigned to this resource would be assigned to this resource
129       * (ie the unused discontiguous space could just as likely be assigned
130       * to another competing resource).
131       *
132       * @return The number of available physical pages for this resource.
133       */
134       public abstract int getAvailablePhysicalPages();
135    
136      /**
137       * Reserve pages.<p>
138       *
139       * The role of reserving pages is that it allows the request to be
140       * noted as pending (the difference between committed and reserved
141       * indicates pending requests).  If the request would exceed the
142       * page budget then the caller must poll in case a GC is necessary.
143       *
144       * @param pages The number of pages requested
145       * @return True if the page budget could satisfy the request.
146       */
147      @Inline
148      public final boolean reservePages(int pages) {
149        lock();
150        required += adjustForMetaData(pages);
151        reserved = committed + required;
152        boolean satisfied = reserved <= pageBudget;
153        unlock();
154        return satisfied;
155      }
156    
157      /**
158       * Remove a request to the space.
159       *
160       * @param pages The number of pages in the request.
161       */
162      @Inline
163      public final void clearRequest(int pages) {
164        lock();
165        required -= adjustForMetaData(pages);
166        unlock();
167      }
168    
169      /**
170       * Reserve pages unconditionally.<p>
171       *
172       * An example of where this is useful is in cases where it is
173       * desirable to put some space aside as head-room.  By
174       * unconditionally reserving the pages, the pages are counted
175       * against the collectors budget.  When the space is actually
176       * needed, the pages can be unconditionally released, freeing
177       * the pages for other purposes.
178       *
179       * @param pages The number of pages to be unconditionally
180       * reserved.
181       */
182      public final void unconditionallyReservePages(int pages) {
183        lock();
184        committed += pages;
185        reserved += pages;
186        unlock();
187      }
188    
189      /**
190       * Release pages unconditionally.<p>
191       *
192       * This call allows pages to be unconditionally removed from
193       * the collectors page budget.
194       *
195       * @see #unconditionallyReservePages
196       * @param pages The number of pages to be unconditionally
197       * released.
198       */
199      public final void unconditionallyReleasePages(int pages) {
200        lock();
201        committed -= pages;
202        reserved -= pages;
203        unlock();
204      }
205    
206      abstract Address allocPages(int pages);
207    
208      /**
209       * Adjust a page request to include metadata requirements for a request
210       * of the given size. This must be a pure function, that is it does not
211       * depend on the state of the PageResource.
212       *
213       * @param pages The size of the pending allocation in pages
214       * @return The number of required pages, inclusive of any metadata
215       */
216      public abstract int adjustForMetaData(int pages);
217    
218      /**
219       * Allocate pages in virtual memory, returning zero on failure.<p>
220       *
221       * If the request cannot be satisfied, zero is returned and it
222       * falls to the caller to trigger the GC.
223       *
224       * Call <code>allocPages</code> (subclass) to find the pages in
225       * virtual memory.  If successful then commit the pending page
226       * request and return the address of the first page.
227       *
228       * @param pages The number of pages requested
229       * @return The address of the first of <code>pages</code> pages, or
230       * zero on failure.
231       */
232      @Inline
233      public final Address getNewPages(int pages) {
234        return allocPages(pages);
235      }
236    
237      /**
238       * Commit pages to the page budget.  This is called after
239       * successfully determining that the request can be satisfied by
240       * both the page budget and virtual memory.  This simply accounts
241       * for the descrepency between <code>committed</code> and
242       * <code>reserved</code> while the request was pending.
243       *
244       * This *MUST* be called by each PageResource during the
245       * allocPages, and the caller must hold the lock.
246       *
247       * @param requestedPages The number of pages from this request
248       * @param totalPages The number of pages
249       */
250      protected void commitPages(int requestedPages, int totalPages) {
251        int predictedPages = adjustForMetaData(requestedPages);
252        int delta = totalPages - predictedPages;
253        required -= predictedPages;
254        reserved += delta;
255        committed += totalPages;
256        if (!Plan.gcInProgress())
257          addToCommitted(totalPages); // only count mutator pages
258      }
259    
260      /**
261       * Return the number of reserved pages
262       *
263       * @return The number of reserved pages.
264       */
265      public final int reservedPages() { return reserved; }
266    
267      /**
268       * Return the number of committed pages
269       *
270       * @return The number of committed pages.
271       */
272      public final int committedPages() { return committed; }
273    
274      /**
275       * Return the number of required pages
276       *
277       * @return The number of required pages.
278       */
279      public final int requiredPages() { return required; }
280    
281      /**
282       * Return the cumulative number of committed pages
283       *
284       * @return The cumulative number of committed pages.
285       */
286      public static long cumulativeCommittedPages() { return cumulativeCommitted; }
287    
288      /**
289       * Add to the total cumulative committed page count.
290       *
291       * @param pages The number of pages to be added.
292       */
293      private static void addToCommitted(int pages) {
294        classLock.acquire();
295        cumulativeCommitted += pages;
296        classLock.release();
297      }
298    
299      /**
300       * Acquire the appropriate lock depending on whether the context is
301       * GC or mutator.
302       */
303      protected final void lock() {
304        if (Plan.gcInProgress())
305          gcLock.acquire();
306        else
307          mutatorLock.acquire();
308      }
309    
310      /**
311       * Release the appropriate lock depending on whether the context is
312       * GC or mutator.
313       */
314      protected final void unlock() {
315        if (Plan.gcInProgress())
316          gcLock.release();
317        else
318          mutatorLock.release();
319      }
320    }