/*
* Copyright 2015 Terracotta, Inc., a Software AG company.
*
* Licensed 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.terracotta.offheapstore.paging;
import org.hamcrest.core.IsNull;
import org.hamcrest.number.OrderingComparison;
import org.junit.Test;
import org.terracotta.offheapstore.buffersource.HeapBufferSource;
import org.terracotta.offheapstore.paging.UpfrontAllocatingPageSource.ThresholdDirection;
import org.terracotta.offheapstore.storage.PointerSize;
import org.terracotta.offheapstore.util.MemoryUnit;
import org.terracotta.offheapstore.util.NoOpLock;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
/**
*
* @author cdennis
*/
public class UpfrontAllocatingPageSourceThresholdTest {
@Test
public void testSimpleRisingThreshold() {
UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), MemoryUnit.KILOBYTES.toBytes(1), MemoryUnit.KILOBYTES.toBytes(1));
final AtomicBoolean rising = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.RISING, 128, new Runnable() {
@Override
public void run() {
rising.set(true);
}
});
assertThat(rising.getAndSet(false), is(false));
Page a = source.allocate(256, false, false, null);
assertThat(rising.getAndSet(false), is(true));
source.free(a);
assertThat(rising.getAndSet(false), is(false));
Page b = source.allocate(256, false, false, null);
assertThat(rising.getAndSet(false), is(true));
Page c = source.allocate(256, false, false, null);
source.free(b);
source.free(c);
assertThat(rising.getAndSet(false), is(false));
}
@Test
public void testSimpleFallingThreshold() {
UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), MemoryUnit.KILOBYTES.toBytes(1), MemoryUnit.KILOBYTES.toBytes(1));
final AtomicBoolean falling = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.FALLING, 128, new Runnable() {
@Override
public void run() {
falling.set(true);
}
});
Page a = source.allocate(256, false, false, null);
assertThat(falling.getAndSet(false), is(false));
source.free(a);
assertThat(falling.getAndSet(false), is(true));
Page b = source.allocate(256, false, false, null);
Page c = source.allocate(256, false, false, null);
source.free(b);
assertThat(falling.getAndSet(false), is(false));
source.free(c);
assertThat(falling.getAndSet(false), is(true));
}
@Test
public void testMultipleRisingThresholds() {
UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), MemoryUnit.KILOBYTES.toBytes(1), MemoryUnit.KILOBYTES.toBytes(1));
final AtomicBoolean rising64 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.RISING, 64, new Runnable() {
@Override
public void run() {
rising64.set(true);
}
});
final AtomicBoolean rising128 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.RISING, 128, new Runnable() {
@Override
public void run() {
rising128.set(true);
}
});
assertThat(rising64.getAndSet(false), is(false));
assertThat(rising128.getAndSet(false), is(false));
Page a = source.allocate(96, false, false, null);
assertThat(rising64.getAndSet(false), is(true));
assertThat(rising128.getAndSet(false), is(false));
Page b = source.allocate(48, false, false, null);
assertThat(rising64.getAndSet(false), is(false));
assertThat(rising128.getAndSet(false), is(true));
source.free(a);
assertThat(rising64.getAndSet(false), is(false));
assertThat(rising128.getAndSet(false), is(false));
Page c = source.allocate(96, false, false, null);
assertThat(rising64.getAndSet(false), is(true));
assertThat(rising128.getAndSet(false), is(true));
source.free(b);
source.free(c);
assertThat(rising64.getAndSet(false), is(false));
assertThat(rising128.getAndSet(false), is(false));
}
@Test
public void testMultipleFallingThresholds() {
UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), MemoryUnit.KILOBYTES.toBytes(1), MemoryUnit.KILOBYTES.toBytes(1));
final AtomicBoolean falling64 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.FALLING, 64, new Runnable() {
@Override
public void run() {
falling64.set(true);
}
});
final AtomicBoolean falling128 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.FALLING, 128, new Runnable() {
@Override
public void run() {
falling128.set(true);
}
});
Page a = source.allocate(48, false, false, null);
Page b = source.allocate(96, false, false, null);
assertThat(falling64.getAndSet(false), is(false));
assertThat(falling128.getAndSet(false), is(false));
source.free(b);
assertThat(falling64.getAndSet(false), is(true));
assertThat(falling128.getAndSet(false), is(true));
source.free(a);
Page c = source.allocate(96, false, false, null);
assertThat(falling64.getAndSet(false), is(false));
assertThat(falling128.getAndSet(false), is(false));
source.free(c);
assertThat(falling64.getAndSet(false), is(true));
assertThat(falling128.getAndSet(false), is(false));
Page d = source.allocate(48, false, false, null);
Page e = source.allocate(96, false, false, null);
assertThat(falling64.getAndSet(false), is(false));
assertThat(falling128.getAndSet(false), is(false));
source.free(d);
assertThat(falling64.getAndSet(false), is(false));
assertThat(falling128.getAndSet(false), is(true));
source.free(e);
assertThat(falling64.getAndSet(false), is(true));
assertThat(falling128.getAndSet(false), is(false));
}
@Test
public void testRisingAndFallingThresholds() {
UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), MemoryUnit.KILOBYTES.toBytes(1), MemoryUnit.KILOBYTES.toBytes(1));
final AtomicBoolean rising64 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.RISING, 64, new Runnable() {
@Override
public void run() {
rising64.set(true);
}
});
final AtomicBoolean falling64 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.FALLING, 64, new Runnable() {
@Override
public void run() {
falling64.set(true);
}
});
Page a = source.allocate(32, false, false, null);
assertThat(rising64.getAndSet(false), is(false));
assertThat(falling64.getAndSet(false), is(false));
Page b = source.allocate(64, false, false, null);
assertThat(rising64.getAndSet(false), is(true));
assertThat(falling64.getAndSet(false), is(false));
source.free(b);
assertThat(rising64.getAndSet(false), is(false));
assertThat(falling64.getAndSet(false), is(true));
source.free(a);
assertThat(rising64.getAndSet(false), is(false));
assertThat(falling64.getAndSet(false), is(false));
}
@Test
public void testThresholdsDuringSteal() {
UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), MemoryUnit.KILOBYTES.toBytes(1), MemoryUnit.KILOBYTES.toBytes(1));
SimpleOwner owner = new SimpleOwner();
OffHeapStorageArea storage = new OffHeapStorageArea(PointerSize.INT, owner, source, 128, false, true);
owner.bind(storage);
final AtomicBoolean rising512 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.RISING, 512, new Runnable() {
@Override
public void run() {
rising512.set(true);
}
});
final AtomicBoolean falling64 = new AtomicBoolean();
source.addAllocationThreshold(ThresholdDirection.FALLING, 64, new Runnable() {
@Override
public void run() {
falling64.set(true);
}
});
long victim = storage.allocate(128);
assertThat(victim, OrderingComparison.greaterThanOrEqualTo(0L));
assertThat(rising512.getAndSet(false), is(false));
assertThat(falling64.getAndSet(false), is(false));
Page thief = source.allocate(1024, true, false, null);
assertThat(thief, IsNull.notNullValue());
assertThat(storage.getAllocatedMemory(), is(0L));
assertThat(rising512.getAndSet(false), is(true));
assertThat(falling64.getAndSet(false), is(true));
long fail = storage.allocate(128);
assertThat(fail, is(-1L));
assertThat(storage.getAllocatedMemory(), is(0L));
assertThat(rising512.getAndSet(false), is(false));
assertThat(falling64.getAndSet(false), is(false));
source.free(thief);
assertThat(storage.getAllocatedMemory(), is(0L));
assertThat(source.getAllocatedSize(), is(0L));
assertThat(rising512.getAndSet(false), is(false));
assertThat(falling64.getAndSet(false), is(true));
}
@Test
public void testActionThatThrows() {
UpfrontAllocatingPageSource source = new UpfrontAllocatingPageSource(new HeapBufferSource(), MemoryUnit.KILOBYTES.toBytes(1), MemoryUnit.KILOBYTES.toBytes(1));
source.addAllocationThreshold(ThresholdDirection.RISING, 128, new Runnable() {
@Override
public void run() {
throw new Error();
}
});
Page a = source.allocate(256, false, false, null);
assertThat(a, IsNull.notNullValue());
assertThat(source.getAllocatedSize(), is(256L));
source.free(a);
}
static class SimpleOwner implements OffHeapStorageArea.Owner {
private OffHeapStorageArea storage;
public void bind(OffHeapStorageArea storage) {
this.storage = storage;
}
@Override
public boolean evictAtAddress(long address, boolean shrink) {
storage.free(address);
return true;
}
@Override
public Lock writeLock() {
return NoOpLock.INSTANCE;
}
@Override
public boolean isThief() {
return false;
}
@Override
public boolean moved(long shift, long pointer) {
throw new UnsupportedOperationException();
}
@Override
public int sizeOf(long shift) {
throw new UnsupportedOperationException();
}
}
}