/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.utility; import org.mmtk.plan.Plan; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; /** * This is a very simple, generic malloc-free allocator. It works * abstractly, in "units", which the user may associate with some * other allocatable resource (e.g. heap blocks). The user issues * requests for N units and the allocator returns the index of the * first of a contiguous set of N units or fails, returning -1. The * user frees the block of N units by calling <code>free()</code> with * the index of the first unit as the argument.<p> * * Properties/Constraints:<ul> * <li> The allocator consumes one word per allocatable unit (plus * a fixed overhead of about 128 words).</li> * <li> The allocator can only deal with MAX_UNITS units (see below for * the value).</li> * </ul> * * The basic data structure used by the algorithm is a large table, * with one word per allocatable unit. Each word is used in a * number of different ways, some combination of "undefined" (32), * "free/used" (1), "multi/single" (1), "prev" (15), "next" (15) & * "size" (15) where field sizes in bits are in parenthesis. * <pre> * +-+-+-----------+-----------+ * |f|m| prev | next/size | * +-+-+-----------+-----------+ * * - single free unit: "free", "single", "prev", "next" * - single used unit: "used", "single" * - contiguous free units * . first unit: "free", "multi", "prev", "next" * . second unit: "free", "multi", "size" * . last unit: "free", "multi", "size" * - contiguous used units * . first unit: "used", "multi", "prev", "next" * . second unit: "used", "multi", "size" * . last unit: "used", "multi", "size" * - any other unit: undefined * * +-+-+-----------+-----------+ * top sentinel |0|0| tail | head | [-1] * +-+-+-----------+-----------+ * .... * /-------- +-+-+-----------+-----------+ * | |1|1| prev | next | [j] * | +-+-+-----------+-----------+ * | |1|1| | size | [j+1] * free multi +-+-+-----------+-----------+ * unit block | ... | ... * | +-+-+-----------+-----------+ * | |1|1| | size | * >-------- +-+-+-----------+-----------+ * single free unit |1|0| prev | next | * >-------- +-+-+-----------+-----------+ * single used unit |0|0| | * >-------- +-+-+-----------------------+ * | |0|1| | * | +-+-+-----------+-----------+ * | |0|1| | size | * used multi +-+-+-----------+-----------+ * unit block | ... | * | +-+-+-----------+-----------+ * | |0|1| | size | * \-------- +-+-+-----------+-----------+ * .... * +-+-+-----------------------+ * bottom sentinel |0|0| | [N] * +-+-+-----------------------+ * </pre> * The sentinels serve as guards against out of range coalescing * because they both appear as "used" blocks and so will never * coalesce. The top sentinel also serves as the head and tail of * the doubly linked list of free blocks. */ @Uninterruptible public final class GenericFreeList extends BaseGenericFreeList implements Constants { /**************************************************************************** * * Public instance methods */ /** * Constructor * * @param units The number of allocatable units for this free list */ public GenericFreeList(int units) { this(units, units); } /** * Constructor * * @param units The number of allocatable units for this free list * @param grain Units are allocated such that they will never cross this granularity boundary */ public GenericFreeList(int units, int grain) { this(units, grain, 1); } /** * Constructor * * @param units The number of allocatable units for this free list * @param grain Units are allocated such that they will never cross this granularity boundary * @param heads The number of free lists which will share this instance */ public GenericFreeList(int units, int grain, int heads) { this.parent = null; if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(units <= MAX_UNITS && heads <= MAX_HEADS); this.heads = heads; head = -1; // allocate the data structure, including space for top & bottom sentinels table = new int[(units + 1 + heads) << 1]; initializeHeap(units, grain); } /** * Resize the free list for a parent free list. * This must not be called dynamically (ie not after bootstrap). * * @param units The number of allocatable units for this free list * @param grain Units are allocated such that they will never cross this granularity boundary */ @Interruptible public void resizeFreeList(int units, int grain) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(parent == null && !Plan.isInitialized()); table = new int[(units + 1 + heads) << 1]; initializeHeap(units, grain); } /** * Resize the free list for a child free list. * This must not be called dynamically (ie not after bootstrap). */ @Interruptible public void resizeFreeList() { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(parent != null && !Plan.isInitialized()); table = parent.getTable(); } /** * Constructor * * @param parent The parent, owning the data structures this instance will share * @param ordinal The ordinal number of this child */ public GenericFreeList(GenericFreeList parent, int ordinal) { this.parent = parent; this.table = parent.getTable(); this.heads = parent.getHeads(); this.head = -(1 + ordinal); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(-this.head <= this.heads); } /** Getter */ int[] getTable() { return table; } int getHeads() { return heads; } /** * Initialize a unit as a sentinel * * @param unit The unit to be initialized */ protected void setSentinel(int unit) { setLoEntry(unit, NEXT_MASK & unit); setHiEntry(unit, PREV_MASK & unit); } /** * Get the size of a lump of units * * @param unit The first unit in the lump of units * @return The size of the lump of units */ protected int getSize(int unit) { if ((getHiEntry(unit) & MULTI_MASK) == MULTI_MASK) return (getHiEntry(unit + 1) & SIZE_MASK); else return 1; } /** * Set the size of lump of units * * @param unit The first unit in the lump of units * @param size The size of the lump of units */ protected void setSize(int unit, int size) { if (size > 1) { setHiEntry(unit, getHiEntry(unit) | MULTI_MASK); setHiEntry(unit + 1, MULTI_MASK | size); setHiEntry(unit + size - 1, MULTI_MASK | size); } else setHiEntry(unit, getHiEntry(unit) & ~MULTI_MASK); } /** * Establish whether a lump of units is free * * @param unit The first or last unit in the lump * @return True if the lump is free */ protected boolean getFree(int unit) { return ((getLoEntry(unit) & FREE_MASK) == FREE_MASK); } /** * Set the "free" flag for a lump of units (both the first and last * units in the lump are set. * * @param unit The first unit in the lump * @param isFree True if the lump is to be marked as free */ protected void setFree(int unit, boolean isFree) { int size; if (isFree) { setLoEntry(unit, getLoEntry(unit) | FREE_MASK); if ((size = getSize(unit)) > 1) setLoEntry(unit + size - 1, getLoEntry(unit + size - 1) | FREE_MASK); } else { setLoEntry(unit, getLoEntry(unit) & ~FREE_MASK); if ((size = getSize(unit)) > 1) setLoEntry(unit + size - 1, getLoEntry(unit + size - 1) & ~FREE_MASK); } } /** * Get the next lump in the doubly linked free list * * @param unit The index of the first unit in the current lump * @return The index of the first unit of the next lump of units in the list */ protected int getNext(int unit) { int next = getHiEntry(unit) & NEXT_MASK; return (next <= MAX_UNITS) ? next : head; } /** * Set the next lump in the doubly linked free list * * @param unit The index of the first unit in the lump to be set * @param next The value to be set. */ protected void setNext(int unit, int next) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((next >= -heads) && (next <= MAX_UNITS)); int oldValue = getHiEntry(unit); int newValue = (oldValue & ~NEXT_MASK) | (next & NEXT_MASK); setHiEntry(unit, newValue); } /** * Get the previous lump in the doubly linked free list * * @param unit The index of the first unit in the current lump * @return The index of the first unit of the previous lump of units * in the list */ protected int getPrev(int unit) { int prev = getLoEntry(unit) & PREV_MASK; return (prev <= MAX_UNITS) ? prev : head; } /** * Set the previous lump in the doubly linked free list * * @param unit The index of the first unit in the lump to be set * @param prev The value to be set. */ protected void setPrev(int unit, int prev) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((prev >= -heads) && (prev <= MAX_UNITS)); int oldValue = getLoEntry(unit); int newValue = (oldValue & ~PREV_MASK) | (prev & PREV_MASK); setLoEntry(unit, newValue); } /** * Set the uncoalescable bit associated with this unit. * This ensures this unit cannot be coalesced with units below * it. * * @param unit The unit whose uncoalescable bit is to be set */ public void setUncoalescable(int unit) { setLoEntry(unit, getLoEntry(unit) | COALESC_MASK); } /** * Clear the uncoalescable bit associated with this unit. * This allows this unit to be coalesced with units below * it. * * @param unit The unit whose uncoalescable bit is to be cleared */ public void clearUncoalescable(int unit) { setLoEntry(unit, getLoEntry(unit) & ~COALESC_MASK); } /** * Return true if this unit may be coalesced with the unit below it. * * @param unit The unit in question * @return True if this unit may be coalesced with the unit below it. */ @Override public boolean isCoalescable(int unit) { return (getLoEntry(unit) & COALESC_MASK) == 0; } /** * Get the lump to the "left" of the current lump (i.e. "above" it) * * @param unit The index of the first unit in the lump in question * @return The index of the first unit in the lump to the * "left"/"above" the lump in question. */ protected int getLeft(int unit) { if ((getHiEntry(unit - 1) & MULTI_MASK) == MULTI_MASK) return unit - (getHiEntry(unit - 1) & SIZE_MASK); else return unit - 1; } /** * Get the contents of an entry * * @param unit The index of the unit * @return The contents of the unit */ private int getLoEntry(int unit) { return table[(unit + heads) << 1]; } private int getHiEntry(int unit) { return table[((unit + heads) << 1) + 1]; } /** * Set the contents of an entry * * @param unit The index of the unit * @param value The contents of the unit */ private void setLoEntry(int unit, int value) { table[(unit + heads) << 1] = value; } private void setHiEntry(int unit, int value) { table[((unit + heads) << 1) + 1] = value; } private static final int TOTAL_BITS = 32; private static final int UNIT_BITS = (TOTAL_BITS - 2); public static final int MAX_UNITS = (int) (((((long) 1) << UNIT_BITS) - 1) - MAX_HEADS - 1); private static final int NEXT_MASK = (int) ((((long) 1) << UNIT_BITS) - 1); private static final int PREV_MASK = (int) ((((long) 1) << UNIT_BITS) - 1); private static final int FREE_MASK = 1 << (TOTAL_BITS - 1); private static final int MULTI_MASK = 1 << (TOTAL_BITS - 1); private static final int COALESC_MASK = 1 << (TOTAL_BITS - 2); private static final int SIZE_MASK = (int) ((((long) 1) << UNIT_BITS) - 1); private int[] table; private final GenericFreeList parent; }