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