/*
* 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.ignite.internal.processors.platform.memory;
import org.apache.ignite.internal.util.GridUnsafe;
/**
* Utility classes for memory management.
*/
public class PlatformMemoryUtils {
/** Header length. */
public static final int POOL_HDR_LEN = 64;
/** Pool header offset: first memory chunk. */
public static final int POOL_HDR_OFF_MEM_1 = 0;
/** Pool header offset: second memory chunk. */
public static final int POOL_HDR_OFF_MEM_2 = 20;
/** Pool header offset: third memory chunk. */
public static final int POOL_HDR_OFF_MEM_3 = 40;
/** Memory chunk header length. */
public static final int MEM_HDR_LEN = 20;
/** Offset: capacity. */
public static final int MEM_HDR_OFF_CAP = 8;
/** Offset: length. */
public static final int MEM_HDR_OFF_LEN = 12;
/** Offset: flags. */
public static final int MEM_HDR_OFF_FLAGS = 16;
/** Flag: external. */
public static final int FLAG_EXT = 0x1;
/** Flag: pooled. */
public static final int FLAG_POOLED = 0x2;
/** Flag: whether this pooled memory chunk is acquired. */
public static final int FLAG_ACQUIRED = 0x4;
/** --- COMMON METHODS. --- */
/**
* Gets data pointer for the given memory chunk.
*
* @param memPtr Memory pointer.
* @return Data pointer.
*/
public static long data(long memPtr) {
return GridUnsafe.getLong(memPtr);
}
/**
* Gets capacity for the given memory chunk.
*
* @param memPtr Memory pointer.
* @return Capacity.
*/
public static int capacity(long memPtr) {
return GridUnsafe.getInt(memPtr + MEM_HDR_OFF_CAP);
}
/**
* Sets capacity for the given memory chunk.
*
* @param memPtr Memory pointer.
* @param cap Capacity.
*/
public static void capacity(long memPtr, int cap) {
assert !isExternal(memPtr) : "Attempt to update external memory chunk capacity: " + memPtr;
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_CAP, cap);
}
/**
* Gets length for the given memory chunk.
*
* @param memPtr Memory pointer.
* @return Length.
*/
public static int length(long memPtr) {
return GridUnsafe.getInt(memPtr + MEM_HDR_OFF_LEN);
}
/**
* Sets length for the given memory chunk.
*
* @param memPtr Memory pointer.
* @param len Length.
*/
public static void length(long memPtr, int len) {
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_LEN, len);
}
/**
* Gets flags for the given memory chunk.
*
* @param memPtr Memory pointer.
* @return Flags.
*/
public static int flags(long memPtr) {
return GridUnsafe.getInt(memPtr + MEM_HDR_OFF_FLAGS);
}
/**
* Sets flags for the given memory chunk.
*
* @param memPtr Memory pointer.
* @param flags Flags.
*/
public static void flags(long memPtr, int flags) {
assert !isExternal(memPtr) : "Attempt to update external memory chunk flags: " + memPtr;
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_FLAGS, flags);
}
/**
* Check whether this memory chunk is external.
*
* @param memPtr Memory pointer.
* @return {@code True} if owned by native platform.
*/
public static boolean isExternal(long memPtr) {
return isExternal(flags(memPtr));
}
/**
* Check whether flags denote that this memory chunk is external.
*
* @param flags Flags.
* @return {@code True} if owned by native platform.
*/
public static boolean isExternal(int flags) {
return (flags & FLAG_EXT) == FLAG_EXT;
}
/**
* Check whether this memory chunk is pooled.
*
* @param memPtr Memory pointer.
* @return {@code True} if pooled.
*/
public static boolean isPooled(long memPtr) {
return isPooled(flags(memPtr));
}
/**
* Check whether flags denote pooled memory chunk.
*
* @param flags Flags.
* @return {@code True} if pooled.
*/
public static boolean isPooled(int flags) {
return (flags & FLAG_POOLED) != 0;
}
/**
* Check whether this memory chunk is pooled and acquired.
*
* @param memPtr Memory pointer.
* @return {@code True} if pooled and acquired.
*/
public static boolean isAcquired(long memPtr) {
return isAcquired(flags(memPtr));
}
/**
* Check whether flags denote pooled and acquired memory chunk.
*
* @param flags Flags.
* @return {@code True} if acquired.
*/
public static boolean isAcquired(int flags) {
assert isPooled(flags);
return (flags & FLAG_ACQUIRED) != 0;
}
/** --- UNPOOLED MEMORY MANAGEMENT. --- */
/**
* Allocate unpooled memory chunk.
*
* @param cap Minimum capacity.
* @return New memory pointer.
*/
public static long allocateUnpooled(int cap) {
assert cap > 0;
long memPtr = GridUnsafe.allocateMemory(MEM_HDR_LEN);
long dataPtr = GridUnsafe.allocateMemory(cap);
GridUnsafe.putLong(memPtr, dataPtr); // Write address.
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_CAP, cap); // Write capacity.
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_LEN, 0); // Write length.
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_FLAGS, 0); // Write flags.
return memPtr;
}
/**
* Reallocate unpooled memory chunk.
*
* @param memPtr Memory pointer.
* @param cap Minimum capacity.
*/
public static void reallocateUnpooled(long memPtr, int cap) {
assert cap > 0;
assert !isExternal(memPtr) : "Attempt to reallocate external memory chunk directly: " + memPtr;
assert !isPooled(memPtr) : "Attempt to reallocate pooled memory chunk directly: " + memPtr;
long dataPtr = data(memPtr);
long newDataPtr = GridUnsafe.reallocateMemory(dataPtr, cap);
if (dataPtr != newDataPtr)
GridUnsafe.putLong(memPtr, newDataPtr); // Write new data address if needed.
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_CAP, cap); // Write new capacity.
}
/**
* Release unpooled memory chunk.
*
* @param memPtr Memory pointer.
*/
public static void releaseUnpooled(long memPtr) {
assert !isExternal(memPtr) : "Attempt to release external memory chunk directly: " + memPtr;
assert !isPooled(memPtr) : "Attempt to release pooled memory chunk directly: " + memPtr;
GridUnsafe.freeMemory(data(memPtr));
GridUnsafe.freeMemory(memPtr);
}
/** --- POOLED MEMORY MANAGEMENT. --- */
/**
* Allocate pool memory.
*
* @return Pool pointer.
*/
public static long allocatePool() {
long poolPtr = GridUnsafe.allocateMemory(POOL_HDR_LEN);
GridUnsafe.setMemory(poolPtr, POOL_HDR_LEN, (byte)0);
flags(poolPtr + POOL_HDR_OFF_MEM_1, FLAG_POOLED);
flags(poolPtr + POOL_HDR_OFF_MEM_2, FLAG_POOLED);
flags(poolPtr + POOL_HDR_OFF_MEM_3, FLAG_POOLED);
return poolPtr;
}
/**
* Release pool memory.
*
* @param poolPtr Pool pointer.
*/
public static void releasePool(long poolPtr) {
// Clean predefined memory chunks.
long mem = GridUnsafe.getLong(poolPtr + POOL_HDR_OFF_MEM_1);
if (mem != 0)
GridUnsafe.freeMemory(mem);
mem = GridUnsafe.getLong(poolPtr + POOL_HDR_OFF_MEM_2);
if (mem != 0)
GridUnsafe.freeMemory(mem);
mem = GridUnsafe.getLong(poolPtr + POOL_HDR_OFF_MEM_3);
if (mem != 0)
GridUnsafe.freeMemory(mem);
// Clean pool chunk.
GridUnsafe.freeMemory(poolPtr);
}
/**
* Allocate pooled memory chunk.
*
* @param poolPtr Pool pointer.
* @param cap Capacity.
* @return Cross-platform memory pointer or {@code 0} in case there are no free memory chunks in the pool.
*/
public static long allocatePooled(long poolPtr, int cap) {
long memPtr1 = poolPtr + POOL_HDR_OFF_MEM_1;
if (isAcquired(memPtr1)) {
long memPtr2 = poolPtr + POOL_HDR_OFF_MEM_2;
if (isAcquired(memPtr2)) {
long memPtr3 = poolPtr + POOL_HDR_OFF_MEM_3;
if (isAcquired(memPtr3))
return 0L;
else {
allocatePooled0(memPtr3, cap);
return memPtr3;
}
}
else {
allocatePooled0(memPtr2, cap);
return memPtr2;
}
}
else {
allocatePooled0(memPtr1, cap);
return memPtr1;
}
}
/**
* Internal pooled memory chunk allocation routine.
*
* @param memPtr Memory pointer.
* @param cap Capacity.
*/
private static void allocatePooled0(long memPtr, int cap) {
assert !isExternal(memPtr);
assert isPooled(memPtr);
assert !isAcquired(memPtr);
long data = GridUnsafe.getLong(memPtr);
if (data == 0) {
// First allocation of the chunk.
data = GridUnsafe.allocateMemory(cap);
GridUnsafe.putLong(memPtr, data);
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_CAP, cap);
}
else {
// Ensure that we have enough capacity.
int curCap = capacity(memPtr);
if (cap > curCap) {
data = GridUnsafe.reallocateMemory(data, cap);
GridUnsafe.putLong(memPtr, data);
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_CAP, cap);
}
}
flags(memPtr, FLAG_POOLED | FLAG_ACQUIRED);
}
/**
* Reallocate pooled memory chunk.
*
* @param memPtr Memory pointer.
* @param cap Minimum capacity.
*/
public static void reallocatePooled(long memPtr, int cap) {
assert !isExternal(memPtr);
assert isPooled(memPtr);
assert isAcquired(memPtr);
long data = GridUnsafe.getLong(memPtr);
assert data != 0;
int curCap = capacity(memPtr);
if (cap > curCap) {
data = GridUnsafe.reallocateMemory(data, cap);
GridUnsafe.putLong(memPtr, data);
GridUnsafe.putInt(memPtr + MEM_HDR_OFF_CAP, cap);
}
}
/**
* Release pooled memory chunk.
*
* @param memPtr Memory pointer.
*/
public static void releasePooled(long memPtr) {
assert !isExternal(memPtr);
assert isPooled(memPtr);
assert isAcquired(memPtr);
flags(memPtr, flags(memPtr) ^ FLAG_ACQUIRED);
}
/** --- UTILITY STUFF. --- */
/**
* Reallocate arbitrary memory chunk.
*
* @param memPtr Memory pointer.
* @param cap Capacity.
*/
public static void reallocate(long memPtr, int cap) {
int flags = flags(memPtr);
if (isPooled(flags))
reallocatePooled(memPtr, cap);
else {
assert !isExternal(flags);
reallocateUnpooled(memPtr, cap);
}
}
/**
* Constructor.
*/
private PlatformMemoryUtils() {
// No-op.
}
}