1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
 
21
package org.jnode.vm;
22
23
import org.jnode.annotation.MagicPermission;
24
import org.jnode.annotation.SharedStatics;
25
import org.jnode.annotation.Uninterruptible;
26
import org.jnode.vm.scheduler.VmProcessor;
27
import org.jnode.vm.scheduler.VmThread;
28
import org.vmmagic.unboxed.Address;
29
import org.vmmagic.unboxed.Extent;
30
import org.vmmagic.unboxed.Word;
31
32
/**
33
 * This class is used to allocate and free fixed size blocks of memory. This
34
 * memory is not garbage collected, nor is each block addressable as a java
35
 * object. Since this manager is used by the Object allocation, do not allocate
36
 * object in this class.
37
 *
38
 * @author Ewout Prangsma (epr@users.sourceforge.net)
39
 */
40
@MagicPermission
41
@Uninterruptible
42
@SharedStatics
43
public final class MemoryBlockManager extends VmSystemObject {
44
45
    /**
46
     * Size of a memory block.
47
     */
48
    static final int BLOCK_SIZE_SHIFT = 16;
49
50
    static final int BLOCK_SIZEa = (1 << BLOCK_SIZE_SHIFT);
51
52
    /**
53
     * Inclusive start address of first block
54
     */
55
    private static Address startPtr;
56
57
    /**
58
     * Exclusive end address of last block
59
     */
60
    private static Address endPtr;
61
62
    /**
63
     * Address of lock to my structures
64
     */
65
    private static Address lockPtr;
66
67
    /**
68
     * Address of usage bitmap
69
     */
70
    private static Address bitmapPtr;
71
72
    /**
73
     * Total number of blocks
74
     */
75
    private static Word blockCount;
76
77
    /**
78
     * Has this class be initialized yet
79
     */
80
    private static boolean initialized;
81
82
    /**
83
     * Number of allocated blocks
84
     */
85
    private static Word allocatedBlocks;
86
87
    /**
88
     * Number of next allocatable block
89
     */
90
    private static Word nextBlockNr;
91
92
    /* Predictive Compilation */
93
    private static final boolean DBG = false;
94
95
    /**
96
     * Allocate a new block of memory at blockSize large. The actual size is
97
     * aligned on BLOCK_SIZE.
98
     *
99
     * @param blockSize
100
     * @return The address of the start of the block, or null when no memory is
101
     *         available.
102
     */
103
    static final Address allocateBlock(Extent blockSize) {
104
        if (!initialized) {
105
            initialize();
106
        }
107
108
        if (DBG) {
109
            Unsafe.debug("allocateSize and BlockCount ");
110
            Unsafe.debug(blockSize.toInt());
111
            Unsafe.debug(" : ");
112
            Unsafe.debug(blockAlign(blockSize.toWord(), true).rshl(BLOCK_SIZE_SHIFT));
113
            Unsafe.debug("\n");
114
        }
115
116
        enter();
117
        try {
118
            // Calculate the number of blocks needed
119
            final Word reqBlockCount = blockAlign(blockSize.toWord(), true).rshl(BLOCK_SIZE_SHIFT);
120
            // Find a large enough series of blocks
121
            final Word nr = findFreeBlocks(reqBlockCount);
122
            if (nr.isMax()) {
123
                Unsafe.debug("ret null.");
124
                Unsafe.debug(blockSize.toInt());
125
                // Unsafe.debug("allocated blocks");
126
                // Unsafe.debug(allocatedBlocks);
127
                // Unsafe.debug("total blocks"); Unsafe.debug(blockCount);
128
                VmProcessor.current().getArchitecture().getStackReader().debugStackTrace();
129
                // Unsafe.die("allocateBlock");
130
                return null;
131
            }
132
            // Mark all blocks as in use
133
            for (Word i = Word.zero(); i.LT(reqBlockCount); i = i.add(1)) {
134
                setInUse(nr.add(i), true);
135
            }
136
            // Return the address of block "nr".
137
            allocatedBlocks = allocatedBlocks.add(reqBlockCount);
138
            nextBlockNr = nr.add(reqBlockCount);
139
            return startPtr.add(nr.lsh(BLOCK_SIZE_SHIFT));
140
        } finally {
141
            exit();
142
        }
143
    }
144
145
    /**
146
     * Free a previously allocated new block of memory at blockSize large. The
147
     * actual size is aligned on BLOCK_SIZE.
148
     *
149
     * @param ptr       The block address as returned by allocateBlock .
150
     * @param blockSize The size of the block as given to allocateBlock.
151
     */
152
    static final void freeBlock(Address ptr, Extent blockSize) {
153
        enter();
154
        try {
155
            // Calculate the block number
156
            final Word nr = ptr.toWord().sub(startPtr.toWord()).rshl(BLOCK_SIZE_SHIFT);
157
            // Calculate the number of blocks
158
            final Word reqBlockCount = blockAlign(blockSize.toWord(), true).rshl(BLOCK_SIZE_SHIFT);
159
            // Mark all blocks as free
160
            for (Word i = Word.zero(); i.LT(reqBlockCount); i = i.add(1)) {
161
                setInUse(nr.add(i), false);
162
            }
163
            allocatedBlocks = allocatedBlocks.sub(reqBlockCount);
164
            if (nr.LT(nextBlockNr)) {
165
                nextBlockNr = nr;
166
            }
167
        } finally {
168
            exit();
169
        }
170
    }
171
172
    /**
173
     * Gets the size of non-allocated memory blocks.
174
     *
175
     * @return The free size in bytes
176
     */
177
    public static long getFreeMemory() {
178
        return blockCount.sub(allocatedBlocks).lsh(BLOCK_SIZE_SHIFT).toLong();
179
    }
180
181
    /**
182
     * Find the first free memory block that is following by freeBlockCount-1
183
     * free memory blocks.
184
     *
185
     * @param freeBlockCount
186
     * @return The block number of the first block, or Word.max() if not found.
187
     */
188
    private static Word findFreeBlocks(Word freeBlockCount) {
189
        final Word max = blockCount.sub(freeBlockCount);
190
        Word nr = nextBlockNr;
191
        while (nr.LT(max)) {
192
            boolean inUse = false;
193
            Word i;
194
            for (i = Word.zero(); i.LT(freeBlockCount) && (!inUse); i = i.add(1)) {
195
                inUse |= isInUse(nr.add(i));
196
            }
197
            if (!inUse) {
198
                // We found it
199
                return nr;
200
            } else {
201
                // Unsafe.debug("nr"); Unsafe.debug(nr);
202
                // Unsafe.debug("i"); Unsafe.debug(i);
203
                // We came across an used block
204
                nr = nr.add(i).add(1);
205
            }
206
        }
207
        // Unsafe.debug("ret -1"); Unsafe.die();
208
        return Word.max();
209
    }
210
211
    /**
212
     * Is a block identified by it blockNr [0..blockCount-1] already used.
213
     *
214
     * @param blockNr
215
     * @return boolean
216
     */
217
    private static final boolean isInUse(Word blockNr) {
218
        final Word offset = blockNr.rshl(3); // we still need a byte offset
219
        final int mask = (1 << blockNr.and(Word.fromIntZeroExtend(7)).toInt());
220
        final Address ptr = bitmapPtr.add(offset);
221
        final int v = ptr.loadByte() & 0xFF;
222
        return ((v & mask) == mask);
223
    }
224
225
    /**
226
     * Is a block identified by it blockNr [0..blockCount-1] already used.
227
     *
228
     * @param blockNr
229
     */
230
    private static final void setInUse(Word blockNr, boolean inUse) {
231
        final Word offset = blockNr.rshl(3); // we still need a byte offset
232
        final int mask = (1 << blockNr.and(Word.fromIntZeroExtend(7)).toInt());
233
        // final int mask = (1 << blockNr);
234
        final Address ptr = bitmapPtr.add(offset);
235
        int v = ptr.loadByte();
236
        if (inUse) {
237
            v |= mask;
238
        } else {
239
            v &= ~mask;
240
        }
241
        ptr.store((byte) v);
242
    }
243
244
    /**
245
     * Initialize this manager.
246
     */
247
    private static void initialize() {
248
        Unsafe.debug("Initialize MemoryBlockManager\n");
249
250
        startPtr = blockAlign(Unsafe.getMemoryStart().toWord(), true).toAddress();
251
        endPtr = blockAlign(Unsafe.getMemoryEnd().toWord(), false).toAddress();
252
253
        Unsafe.debug("Start end: ");
254
        Unsafe.debug(startPtr);
255
        Unsafe.debug(endPtr);
256
        Unsafe.debug('\n');
257
258
        final Extent size = endPtr.toWord().sub(startPtr.toWord()).toExtent();
259
        Unsafe.debug("Size     : ");
260
        Unsafe.debug(size);
261
        Unsafe.debug('\n');
262
263
        blockCount = size.toWord().rshl(BLOCK_SIZE_SHIFT);
264
        // Create a lock (4 bytes) and usage bitmap at the front of the memory
265
        // region
266
        final Extent rawBitmapSize = blockCount.rshl(3).toExtent();
267
        // final long rawBitmapSize = blockCount;
268
        final Extent bitmapSize = blockAlign(rawBitmapSize.toWord().add(4), true).toExtent();
269
        if (false) {
270
            Unsafe.debug("startPtr:");
271
            Unsafe.debug(startPtr);
272
        }
273
        lockPtr = startPtr;
274
        if (DBG) {
275
            Unsafe.debug("lockPtr:");
276
            Unsafe.debug(lockPtr);
277
            Unsafe.debug("bitmapSize:");
278
            Unsafe.debug(bitmapSize);
279
        }
280
        bitmapPtr = startPtr.add(4);
281
        // Clear the lock & bitmap size
282
        clear(lockPtr, bitmapSize);
283
        // Now shift the startptr.
284
        startPtr = startPtr.add(bitmapSize);
285
        blockCount = blockCount.sub(bitmapSize.toWord().rshl(BLOCK_SIZE_SHIFT));
286
        allocatedBlocks = Word.zero();
287
        // Mark as initialized
288
        initialized = true;
289
290
        // Unsafe.debug("BitmapPtr ");
291
        // Unsafe.debug(Unsafe.addressToLong(bitmapPtr));
292
        // Unsafe.debug("LockPtr ");
293
        // Unsafe.debug(Unsafe.addressToLong(lockPtr));
294
        // Unsafe.debug("StartPtr "); Unsafe.debug(startPtr);
295
        // Unsafe.debug("BitmapSize "); Unsafe.debug(bitmapSize);
296
        // Unsafe.debug("Block count "); Unsafe.debug(blockCount);
297
        // Unsafe.die();
298
        if (true) {
299
            Unsafe.debug("end of initialize.");
300
        }
301
        Unsafe.debug('\n');
302
    }
303
304
    /**
305
     * Claim access to my structures. This is done without using java
306
     * monitorenter/exit instructions, since they may need a memory allocation.
307
     */
308
    private static final void enter() {
309
        while (!lockPtr.attempt(0, 1)) {
310
            Unsafe.debug(lockPtr.loadInt());
311
            VmThread.yield();
312
        }
313
    }
314
315
    /**
316
     * Release access to my structures.
317
     */
318
    private static final void exit() {
319
        lockPtr.store((int) 0);
320
    }
321
322
    /**
323
     * Align the given address on the boundary on BLOCK_SIZE.
324
     *
325
     * @param ptr
326
     * @param roundup
327
     * @return The aligned address as long
328
     */
329
    private static Word blockAlign(Word ptr, boolean roundup) {
330
        final Word blockSizeM1 = Word.fromIntSignExtend(BLOCK_SIZEa - 1);
331
        if (roundup) {
332
            ptr = ptr.add(blockSizeM1);
333
        }
334
        return ptr.and(blockSizeM1.not());
335
    }
336
337
    /**
338
     * Clear a large area of memory. Fill the memory with zeros.
339
     *
340
     * @param ptr
341
     * @param size
342
     */
343
    private static void clear(Address ptr, Extent size) {
344
        Unsafe.clear(ptr, size);
345
    }
346
}