blob: 95a0645dde07e035c3124de33553d6b48a53d1e8 [file] [log] [blame]
/*
* 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;
}