/*
* 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.util.offheap.unsafe;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.util.GridRandom;
import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.jsr166.LongAdder8;
/**
* Tests unsafe memory.
*/
public class GridUnsafeMemorySelfTest extends GridCommonAbstractTest {
/** */
public void testBuffers() {
ByteBuffer b1 = GridUnsafe.allocateBuffer(10);
ByteBuffer b2 = GridUnsafe.allocateBuffer(20);
assertEquals(GridUnsafe.NATIVE_BYTE_ORDER, b2.order());
assertTrue(b2.isDirect());
assertEquals(20, b2.capacity());
assertEquals(20, b2.limit());
assertEquals(0, b2.position());
assertEquals(GridUnsafe.NATIVE_BYTE_ORDER, b1.order());
assertTrue(b1.isDirect());
assertEquals(10, b1.capacity());
assertEquals(10, b1.limit());
assertEquals(0, b1.position());
b1.putLong(1L);
b1.putShort((short)7);
b2.putLong(2L);
GridUnsafe.freeBuffer(b1);
b2.putLong(3L);
b2.putInt(9);
for (int i = 0; i <= 16; i++)
b2.putInt(i, 100500);
GridUnsafe.freeBuffer(b2);
}
/**
* @throws Exception If failed.
*/
public void testBytes() throws Exception {
GridUnsafeMemory mem = new GridUnsafeMemory(64);
String s = "123";
byte[] bytes = s.getBytes();
int size = bytes.length * 2;
long addr = mem.allocate(size);
try {
mem.writeBytes(addr, bytes);
byte[] read = mem.readBytes(addr, bytes.length);
assert Arrays.equals(bytes, read);
}
finally {
mem.release(addr, size);
}
}
/**
* @throws Exception If failed.
*/
public void testByte() throws Exception {
GridUnsafeMemory mem = new GridUnsafeMemory(64);
int size = 32;
long addr = mem.allocate(size);
try {
byte b1 = 123;
mem.writeByte(addr, b1);
byte b2 = mem.readByte(addr);
assertEquals(b1, b2);
byte b3 = 11;
mem.writeByteVolatile(addr, b3);
assertEquals(b3, mem.readByteVolatile(addr));
}
finally {
mem.release(addr, size);
}
}
/**
* @throws Exception If failed.
*/
public void testShort() throws Exception {
GridUnsafeMemory mem = new GridUnsafeMemory(64);
int size = 16;
long addr = mem.allocate(size);
try {
short s1 = (short)56777;
mem.writeShort(addr, s1);
assertEquals(s1, mem.readShort(addr));
}
finally {
mem.release(addr, size);
}
}
/**
*
*/
public void testFloat() {
GridUnsafeMemory mem = new GridUnsafeMemory(64);
int size = 32;
long addr = mem.allocate(size);
try {
float f1 = 0.23223f;
mem.writeFloat(addr, f1);
assertEquals(f1, mem.readFloat(addr));
}
finally {
mem.release(addr, size);
}
}
/**
*
*/
public void testDouble() {
GridUnsafeMemory mem = new GridUnsafeMemory(64);
int size = 32;
long addr = mem.allocate(size);
try {
double d1 = 0.2323423;
mem.writeDouble(addr, d1);
assertEquals(d1, mem.readDouble(addr));
}
finally {
mem.release(addr, size);
}
}
/**
* @throws Exception If failed.
*/
public void testInt() throws Exception {
GridUnsafeMemory mem = new GridUnsafeMemory(64);
int size = 32;
long addr = mem.allocate(size);
try {
int i1 = 123;
mem.writeInt(addr, i1);
int i2 = mem.readInt(addr);
assertEquals(i1, i2);
int i3 = 321;
mem.writeIntVolatile(addr, i3);
int i4 = 222;
assertTrue(mem.casInt(addr, i3, i4));
assertFalse(mem.casInt(addr, i3, 0));
assertEquals(i4, mem.readIntVolatile(addr));
}
finally {
mem.release(addr, size);
}
}
/**
* @throws Exception If failed.
*/
public void testLong() throws Exception {
GridUnsafeMemory mem = new GridUnsafeMemory(64);
int size = 32;
long addr = mem.allocate(size);
try {
long l1 = 123456;
mem.writeLong(addr, l1);
long l2 = mem.readLong(addr);
assertEquals(l1, l2);
long l3 = 654321;
mem.writeLongVolatile(addr, l3);
long l4 = 666666;
assertTrue(mem.casLong(addr, l3, l4));
assertFalse(mem.casLong(addr, l3, 0));
assertEquals(l4, mem.readLongVolatile(addr));
}
finally {
mem.release(addr, size);
}
}
/**
* @throws Exception If failed.
*/
public void testGuardedOpsSimple() throws Exception {
final GridUnsafeGuard guard = new GridUnsafeGuard();
final AtomicInteger i = new AtomicInteger();
guard.begin();
guard.finalizeLater(new Runnable() {
@Override
public void run() {
i.incrementAndGet();
}
});
guard.begin();
assertEquals(0, i.get());
guard.end();
assertEquals(0, i.get());
guard.end();
X.println("__ " + guard);
assertEquals(1, i.get());
}
/**
* @throws Exception if failed.
*/
public void testGuardedOps() throws Exception {
final int lineSize = 16;
final int ptrsCnt = 4;
final AtomicReferenceArray<CmpMem> ptrs = new AtomicReferenceArray<>(ptrsCnt * lineSize);
final AtomicBoolean finished = new AtomicBoolean();
final LongAdder8 cntr = new LongAdder8();
final GridUnsafeGuard guard = new GridUnsafeGuard();
GridRandom rnd = new GridRandom();
for (int a = 0; a < 7; a++) {
finished.set(false);
int threads = 2 + rnd.nextInt(37);
int time = rnd.nextInt(5);
X.println("__ starting threads: " + threads + " time: " + time + " sec");
final LongAdder8 locAdder = new LongAdder8();
IgniteInternalFuture<?> fut = multithreadedAsync(new Callable<Object>() {
@Override public Object call() throws Exception {
Random rnd = new GridRandom();
while (!finished.get()) {
int idx = rnd.nextInt(ptrsCnt) * lineSize;
guard.begin();
try {
final CmpMem old;
CmpMem ptr = null;
switch (rnd.nextInt(6)) {
case 0:
ptr = new CmpMem(cntr);
//noinspection fallthrough
case 1:
old = ptrs.getAndSet(idx, ptr);
if (old != null) {
guard.finalizeLater(new Runnable() {
@Override public void run() {
old.deallocate();
}
});
}
break;
case 2:
if (rnd.nextBoolean())
ptr = new CmpMem(cntr);
old = ptrs.getAndSet(idx, ptr);
if (old != null)
guard.releaseLater(old);
break;
default:
old = ptrs.get(idx);
if (old != null)
old.touch();
}
}
finally {
guard.end();
locAdder.increment();
}
}
return null;
}
}, threads);
Thread.sleep(1000 * time);
X.println("__ stopping ops...");
finished.set(true);
fut.get();
X.println("__ stopped, performed ops: " + locAdder.sum());
for (int i = 0; i < ptrs.length(); i++) {
CmpMem ptr = ptrs.getAndSet(i, null);
if (ptr != null) {
ptr.touch();
ptr.deallocate();
}
}
X.println("__ " + guard);
assertEquals(0, cntr.sum());
}
}
private static class CmpMem extends AtomicInteger implements GridUnsafeCompoundMemory {
/** */
private AtomicBoolean deallocated = new AtomicBoolean();
/** */
private LongAdder8 cntr;
/**
* @param cntr Counter.
*/
CmpMem(LongAdder8 cntr) {
this.cntr = cntr;
cntr.increment();
}
public void touch() {
assert !deallocated.get();
}
@Override public void deallocate() {
boolean res = deallocated.compareAndSet(false, true);
assert res;
cntr.add(-get() - 1); // Merged plus this instance.
}
@Override public void merge(GridUnsafeCompoundMemory compound) {
touch();
CmpMem c = (CmpMem)compound;
c.touch();
assert c.get() == 0;
incrementAndGet();
}
}
/**
* @throws Exception If failed.
*/
public void testCompare1() throws Exception {
checkCompare("123");
}
/**
* @throws Exception If failed.
*/
public void testCompare2() throws Exception {
checkCompare("1234567890");
}
/**
* @throws Exception If failed.
*/
public void testCompare3() throws Exception {
checkCompare("12345678901234567890");
}
/**
* @param s String.
* @throws Exception If failed.
*/
public void checkCompare(String s) throws Exception {
byte[] bytes = s.getBytes();
int size = bytes.length + 8;
GridUnsafeMemory mem = new GridUnsafeMemory(size);
for (int i = 0; i < 8; i++) {
long addr = mem.allocate(size);
long ptr = addr + i;
try {
mem.writeBytes(ptr, bytes);
assert mem.compare(ptr, bytes);
byte[] read = mem.readBytes(ptr, bytes.length);
assert Arrays.equals(bytes, read);
}
finally {
mem.release(addr, size);
}
}
}
/**
* @throws Exception If failed.
*/
public void testOutOfMemory() throws Exception {
int cap = 64;
int block = 9;
int allowed = cap / block;
Collection<Long> addrs = new ArrayList<>(allowed);
GridUnsafeMemory mem = new GridUnsafeMemory(cap);
try {
boolean oom = false;
for (int i = 0; i <= allowed; i++) {
boolean reserved = mem.reserve(block);
long addr = mem.allocate(block, true, true);
addrs.add(addr);
if (!reserved) {
assertEquals(i, allowed);
oom = true;
break;
}
}
assertTrue(oom);
}
finally {
for (Long addr : addrs)
mem.release(addr, block);
}
assertEquals(mem.allocatedSize(), 0);
}
}