Package org.jnode.vm

Source Code of org.jnode.vm.MemoryBlockManager

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm;

import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.SharedStatics;
import org.jnode.annotation.Uninterruptible;
import org.jnode.vm.objects.VmSystemObject;
import org.jnode.vm.scheduler.VmProcessor;
import org.jnode.vm.scheduler.VmThread;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Word;

/**
* This class is used to allocate and free fixed size blocks of memory. This
* memory is not garbage collected, nor is each block addressable as a java
* object. Since this manager is used by the Object allocation, do not allocate
* object in this class.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
@Uninterruptible
@SharedStatics
public final class MemoryBlockManager extends VmSystemObject {

    /**
     * Size of a memory block.
     */
    static final int BLOCK_SIZE_SHIFT = 16;

    static final int BLOCK_SIZEa = (1 << BLOCK_SIZE_SHIFT);

    /**
     * Inclusive start address of first block
     */
    private static Address startPtr;

    /**
     * Exclusive end address of last block
     */
    private static Address endPtr;

    /**
     * Address of lock to my structures
     */
    private static Address lockPtr;

    /**
     * Address of usage bitmap
     */
    private static Address bitmapPtr;

    /**
     * Total number of blocks
     */
    private static Word blockCount;

    /**
     * Has this class be initialized yet
     */
    private static boolean initialized;

    /**
     * Number of allocated blocks
     */
    private static Word allocatedBlocks;

    /**
     * Number of next allocatable block
     */
    private static Word nextBlockNr;

    /* Predictive Compilation */
    private static final boolean DBG = false;

    /**
     * Allocate a new block of memory at blockSize large. The actual size is
     * aligned on BLOCK_SIZE.
     *
     * @param blockSize
     * @return The address of the start of the block, or null when no memory is
     *         available.
     */
    static final Address allocateBlock(Extent blockSize) {
        if (!initialized) {
            initialize();
        }

        if (DBG) {
            Unsafe.debug("allocateSize and BlockCount ");
            Unsafe.debug(blockSize.toInt());
            Unsafe.debug(" : ");
            Unsafe.debug(blockAlign(blockSize.toWord(), true).rshl(BLOCK_SIZE_SHIFT));
            Unsafe.debug("\n");
        }

        enter();
        try {
            // Calculate the number of blocks needed
            final Word reqBlockCount = blockAlign(blockSize.toWord(), true).rshl(BLOCK_SIZE_SHIFT);
            // Find a large enough series of blocks
            final Word nr = findFreeBlocks(reqBlockCount);
            if (nr.isMax()) {
                Unsafe.debug("ret null.");
                Unsafe.debug(blockSize.toInt());
                // Unsafe.debug("allocated blocks");
                // Unsafe.debug(allocatedBlocks);
                // Unsafe.debug("total blocks"); Unsafe.debug(blockCount);
                VmProcessor.current().getArchitecture().getStackReader().debugStackTrace();
                // Unsafe.die("allocateBlock");
                return null;
            }
            // Mark all blocks as in use
            for (Word i = Word.zero(); i.LT(reqBlockCount); i = i.add(1)) {
                setInUse(nr.add(i), true);
            }
            // Return the address of block "nr".
            allocatedBlocks = allocatedBlocks.add(reqBlockCount);
            nextBlockNr = nr.add(reqBlockCount);
            return startPtr.add(nr.lsh(BLOCK_SIZE_SHIFT));
        } finally {
            exit();
        }
    }

    /**
     * Free a previously allocated new block of memory at blockSize large. The
     * actual size is aligned on BLOCK_SIZE.
     *
     * @param ptr       The block address as returned by allocateBlock .
     * @param blockSize The size of the block as given to allocateBlock.
     */
    static final void freeBlock(Address ptr, Extent blockSize) {
        enter();
        try {
            // Calculate the block number
            final Word nr = ptr.toWord().sub(startPtr.toWord()).rshl(BLOCK_SIZE_SHIFT);
            // Calculate the number of blocks
            final Word reqBlockCount = blockAlign(blockSize.toWord(), true).rshl(BLOCK_SIZE_SHIFT);
            // Mark all blocks as free
            for (Word i = Word.zero(); i.LT(reqBlockCount); i = i.add(1)) {
                setInUse(nr.add(i), false);
            }
            allocatedBlocks = allocatedBlocks.sub(reqBlockCount);
            if (nr.LT(nextBlockNr)) {
                nextBlockNr = nr;
            }
        } finally {
            exit();
        }
    }

    /**
     * Gets the size of non-allocated memory blocks.
     *
     * @return The free size in bytes
     */
    public static long getFreeMemory() {
        return blockCount.sub(allocatedBlocks).lsh(BLOCK_SIZE_SHIFT).toLong();
    }

    /**
     * Find the first free memory block that is following by freeBlockCount-1
     * free memory blocks.
     *
     * @param freeBlockCount
     * @return The block number of the first block, or Word.max() if not found.
     */
    private static Word findFreeBlocks(Word freeBlockCount) {
        final Word max = blockCount.sub(freeBlockCount);
        Word nr = nextBlockNr;
        while (nr.LT(max)) {
            boolean inUse = false;
            Word i;
            for (i = Word.zero(); i.LT(freeBlockCount) && (!inUse); i = i.add(1)) {
                inUse |= isInUse(nr.add(i));
            }
            if (!inUse) {
                // We found it
                return nr;
            } else {
                // Unsafe.debug("nr"); Unsafe.debug(nr);
                // Unsafe.debug("i"); Unsafe.debug(i);
                // We came across an used block
                nr = nr.add(i).add(1);
            }
        }
        // Unsafe.debug("ret -1"); Unsafe.die();
        return Word.max();
    }

    /**
     * Is a block identified by it blockNr [0..blockCount-1] already used.
     *
     * @param blockNr
     * @return boolean
     */
    private static final boolean isInUse(Word blockNr) {
        final Word offset = blockNr.rshl(3); // we still need a byte offset
        final int mask = (1 << blockNr.and(Word.fromIntZeroExtend(7)).toInt());
        final Address ptr = bitmapPtr.add(offset);
        final int v = ptr.loadByte() & 0xFF;
        return ((v & mask) == mask);
    }

    /**
     * Is a block identified by it blockNr [0..blockCount-1] already used.
     *
     * @param blockNr
     */
    private static final void setInUse(Word blockNr, boolean inUse) {
        final Word offset = blockNr.rshl(3); // we still need a byte offset
        final int mask = (1 << blockNr.and(Word.fromIntZeroExtend(7)).toInt());
        // final int mask = (1 << blockNr);
        final Address ptr = bitmapPtr.add(offset);
        int v = ptr.loadByte();
        if (inUse) {
            v |= mask;
        } else {
            v &= ~mask;
        }
        ptr.store((byte) v);
    }

    /**
     * Initialize this manager.
     */
    private static void initialize() {
        Unsafe.debug("Initialize MemoryBlockManager\n");
        startPtr = blockAlign(Unsafe.getMemoryStart().toWord(), true).toAddress();
        endPtr = blockAlign(Unsafe.getMemoryEnd().toWord(), false).toAddress();

        Unsafe.debug("Start end: ");
        Unsafe.debug(startPtr);
        Unsafe.debug(endPtr);
        Unsafe.debug('\n');

        final Extent size = endPtr.toWord().sub(startPtr.toWord()).toExtent();
        Unsafe.debug("Size     : ");
        Unsafe.debug(size);
        Unsafe.debug('\n');

        blockCount = size.toWord().rshl(BLOCK_SIZE_SHIFT);
        // Create a lock (4 bytes) and usage bitmap at the front of the memory
        // region
        final Extent rawBitmapSize = blockCount.rshl(3).toExtent();
        // final long rawBitmapSize = blockCount;
        final Extent bitmapSize = blockAlign(rawBitmapSize.toWord().add(4), true).toExtent();
        if (false) {
            Unsafe.debug("startPtr:");
            Unsafe.debug(startPtr);
        }
        lockPtr = startPtr;
        if (DBG) {
            Unsafe.debug("lockPtr:");
            Unsafe.debug(lockPtr);
            Unsafe.debug("bitmapSize:");
            Unsafe.debug(bitmapSize);
        }
        bitmapPtr = startPtr.add(4);
        // Clear the lock & bitmap size
        clear(lockPtr, bitmapSize);
        // Now shift the startptr.
        startPtr = startPtr.add(bitmapSize);
        blockCount = blockCount.sub(bitmapSize.toWord().rshl(BLOCK_SIZE_SHIFT));
        allocatedBlocks = Word.zero();
        // Mark as initialized
        initialized = true;

        // Unsafe.debug("BitmapPtr ");
        // Unsafe.debug(Unsafe.addressToLong(bitmapPtr));
        // Unsafe.debug("LockPtr ");
        // Unsafe.debug(Unsafe.addressToLong(lockPtr));
        // Unsafe.debug("StartPtr "); Unsafe.debug(startPtr);
        // Unsafe.debug("BitmapSize "); Unsafe.debug(bitmapSize);
        // Unsafe.debug("Block count "); Unsafe.debug(blockCount);
        // Unsafe.die();
        if (true) {
            Unsafe.debug("end of initialize.");
        }
        Unsafe.debug('\n');
    }

    /**
     * Claim access to my structures. This is done without using java
     * monitorenter/exit instructions, since they may need a memory allocation.
     */
    private static final void enter() {
        while (!lockPtr.attempt(0, 1)) {
            Unsafe.debug(lockPtr.loadInt());
            VmThread.yield();
        }
    }

    /**
     * Release access to my structures.
     */
    private static final void exit() {
        lockPtr.store((int) 0);
    }

    /**
     * Align the given address on the boundary on BLOCK_SIZE.
     *
     * @param ptr
     * @param roundup
     * @return The aligned address as long
     */
    private static Word blockAlign(Word ptr, boolean roundup) {
        final Word blockSizeM1 = Word.fromIntSignExtend(BLOCK_SIZEa - 1);
        if (roundup) {
            ptr = ptr.add(blockSizeM1);
        }
        return ptr.and(blockSizeM1.not());
    }

    /**
     * Clear a large area of memory. Fill the memory with zeros.
     *
     * @param ptr
     * @param size
     */
    private static void clear(Address ptr, Extent size) {
        Unsafe.clear(ptr, size);
    }
}
TOP

Related Classes of org.jnode.vm.MemoryBlockManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.