blob: 57c7c760d01bc051556ff8b4457630e768c3c6b0 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.util.offheap;
import java.io.IOException;
import java.lang.Long;
import java.lang.RuntimeException;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.apache.hadoop.classification.InterfaceAudience.Private;
import org.apache.hadoop.classification.InterfaceStability.Unstable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ByteArrayMemoryManager is a memory manager which keeps all memory on the Java
* heap. It is useful for testing, since it peforms validation of all memory
* accesses and writes. It also can be used if sun.misc.Unsafe is not
* available, although its performance will be less than that of the off-heap
* code.
*/
@Private
@Unstable
public class ByteArrayMemoryManager implements MemoryManager {
static final Logger LOG =
LoggerFactory.getLogger(ByteArrayMemoryManager.class);
private final static long MAX_ADDRESS = 0x3fffffffffffffffL;
private final TreeMap<Long, byte[]> buffers = new TreeMap<Long, byte[]>();
private long curAddress = 1000;
private final String name;
public ByteArrayMemoryManager(String name) {
this.name = name;
LOG.debug("Created {}.", this);
}
@Override
public synchronized void close() throws IOException {
Iterator<Entry<Long, byte[]>> iter = buffers.entrySet().iterator();
if (iter.hasNext()) {
StringBuilder bld = new StringBuilder();
Entry<Long, byte[]> entry = iter.next();
bld.append(entryToString(entry));
int numPrinted = 1;
while (iter.hasNext()) {
if (numPrinted >= 10) {
bld.append("...");
break;
}
bld.append(", ").append(entryToString(entry));
numPrinted++;
}
throw new RuntimeException("There are still unfreed buffers. " +
bld.toString());
}
LOG.debug("Closed {}.", this);
}
private static String entryToString(Entry<Long, byte[]> entry) {
StringBuilder bld = new StringBuilder();
bld.append("Entry(base=0x").append(Long.toHexString(entry.getKey())).
append(", len=0x").append(Long.toHexString(entry.getValue().length)).
append(")");
return bld.toString();
}
@Override
public synchronized long allocate(long size) {
if (curAddress + size > MAX_ADDRESS) {
throw new RuntimeException("Cannot allocate any more memory.");
}
if (size > 0x7fffffff) {
throw new RuntimeException("Attempted to allocate " + size +
" bytes, but we cannot allocate a Java byte array with " +
"more than 2^^31 entries.");
}
long addr = curAddress;
curAddress += size;
byte val[] = new byte[(int)size];
buffers.put(Long.valueOf(addr), val);
LOG.trace("Allocated Entry(base=0x{}, len=0x{})",
Long.toHexString(addr), Long.toHexString(val.length));
return addr;
}
@Override
public synchronized long allocateZeroed(long size) {
// Java byte arrays are always zeroed on construction.
return allocate(size);
}
@Override
public synchronized void free(long addr) {
byte val[] = buffers.remove(Long.valueOf(addr));
if (val == null) {
LOG.error("Attempted to free unallocated address 0x{}",
Long.toHexString(addr));
} else {
LOG.trace("Freed Entry(base=0x{}, len=0x{})",
Long.toHexString(addr), Long.toHexString(val.length));
}
}
private synchronized Entry<Long, byte[]> getEntry(long addr, String op) {
Entry<Long, byte[]> entry = buffers.floorEntry(Long.valueOf(addr));
if (entry == null) {
throw new RuntimeException(op + " unallocated address 0x" +
Long.toHexString(addr));
}
return entry;
}
@Override
public synchronized byte getByte(long addr) {
Entry<Long, byte[]> entry = getEntry(addr, "Accessed");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 1 > arr.length) {
throw new RuntimeException("Attempted to read unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated area " +
"is " + entryToString(entry));
}
int i = (int)off;
return arr[i];
}
@Override
public void putByte(long addr, byte val) {
Entry<Long, byte[]> entry = getEntry(addr, "Wrote to");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 1 > arr.length) {
throw new RuntimeException("Attempted to write to unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated area " +
"is " + entryToString(entry));
}
int i = (int)off;
arr[i] = val;
}
@Override
public synchronized short getShort(long addr) {
Entry<Long, byte[]> entry = getEntry(addr, "Accessed");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 2 > arr.length) {
throw new RuntimeException("Attempted to read unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated " +
"area is " + entryToString(entry));
}
int i = (int)off;
return (short)((arr[i + 0] & 0xff) << 8 |
(arr[i + 1] & 0xff));
}
@Override
public void putShort(long addr, short val) {
Entry<Long, byte[]> entry = getEntry(addr, "Wrote to");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 2 > arr.length) {
throw new RuntimeException("Attempted to write to unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated " +
"area is " + entryToString(entry));
}
int i = (int)off;
arr[i + 0] = (byte)((val >> 8) & 0xff);
arr[i + 1] = (byte)(val & 0xff);
}
@Override
public int getInt(long addr) {
Entry<Long, byte[]> entry = getEntry(addr, "Accessed");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 4 > arr.length) {
throw new RuntimeException("Attempted to read unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated " +
"area is " + entryToString(entry));
}
int i = (int)off;
return arr[i + 0] << 24 |
(arr[i + 1] & 0xff) << 16 |
(arr[i + 2] & 0xff) << 8 |
(arr[i + 3] & 0xff);
}
@Override
public void putInt(long addr, int val) {
Entry<Long, byte[]> entry = getEntry(addr, "Wrote to");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 4 > arr.length) {
throw new RuntimeException("Attempted to write to unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated " +
"area is " + entryToString(entry));
}
int i = (int)off;
arr[i + 0] = (byte)((val >> 24) & 0xff);
arr[i + 1] = (byte)((val >> 16) & 0xff);
arr[i + 2] = (byte)((val >> 8) & 0xff);
arr[i + 3] = (byte)(val & 0xff);
}
@Override
public long getLong(long addr) {
Entry<Long, byte[]> entry = getEntry(addr, "Accessed");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 8 > arr.length) {
throw new RuntimeException("Attempted to read unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated " +
"area is " + entryToString(entry));
}
int i = (int)off;
return (arr[i + 0] & 0xffL) << 56 |
(arr[i + 1] & 0xffL) << 48 |
(arr[i + 2] & 0xffL) << 40 |
(arr[i + 3] & 0xffL) << 32 |
(arr[i + 4] & 0xffL) << 24 |
(arr[i + 5] & 0xffL) << 16 |
(arr[i + 6] & 0xffL) << 8 |
(arr[i + 7] & 0xffL);
}
@Override
public void putLong(long addr, long val) {
Entry<Long, byte[]> entry = getEntry(addr, "Wrote to");
long off = addr - entry.getKey();
byte arr[] = entry.getValue();
if (off + 8 > arr.length) {
throw new RuntimeException("Attempted to write to unallocated memory " +
"at 0x" + Long.toHexString(addr) + ". Closest lower allocated " +
"area is " + entryToString(entry));
}
int i = (int)off;
arr[i + 0] = (byte)((val >> 56) & 0xff);
arr[i + 1] = (byte)((val >> 48) & 0xff);
arr[i + 2] = (byte)((val >> 40) & 0xff);
arr[i + 3] = (byte)((val >> 32) & 0xff);
arr[i + 4] = (byte)((val >> 24) & 0xff);
arr[i + 5] = (byte)((val >> 16) & 0xff);
arr[i + 6] = (byte)((val >> 8) & 0xff);
arr[i + 7] = (byte)(val & 0xff);
}
@Override
public String toString() {
return "ByteArrayMemoryManager(" + name + ")";
}
}