/*
* 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.util.NoSuchElementException;
import java.util.Set;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
import org.apache.ignite.internal.util.lang.GridCloseableIterator;
import org.apache.ignite.internal.util.offheap.GridOffHeapEventListener;
import org.apache.ignite.internal.util.offheap.GridOffHeapEvictListener;
import org.apache.ignite.internal.util.offheap.GridOffHeapMap;
import org.apache.ignite.internal.util.offheap.GridOffHeapPartitionedMap;
import org.apache.ignite.internal.util.typedef.CX2;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.lang.IgniteBiPredicate;
import org.apache.ignite.lang.IgniteBiTuple;
import org.jetbrains.annotations.Nullable;
import org.jsr166.LongAdder8;
/**
* Off-heap map based on {@code Unsafe} implementation.
*/
public class GridUnsafePartitionedMap implements GridOffHeapPartitionedMap {
/** Minimum segment concurrency. */
private static final int MIN_SEGMENT_CONCURRENCY = 4;
/** Array holding maps for each partition. */
private final GridUnsafeMap[] partMap;
/** Total memory. */
private final GridUnsafeMemory mem;
/** Evict closure. */
private GridOffHeapEvictListener evictLsnr;
/** Event listener. */
private GridOffHeapEventListener evtLsnr;
/** Striped LRU policy. */
private final GridUnsafeLru lru;
/** Concurrency. */
private final int concurrency;
/** Load factor. */
private final float load;
/** Partitions. */
private final int parts;
/** */
private final LongAdder8 totalCnt = new LongAdder8();
/**
* @param parts Partitions.
* @param concurrency Concurrency.
* @param load Load factor.
* @param initCap Initial capacity.
* @param totalMem Total memory.
* @param lruStripes LRU stripes.
* @param evictLsnr Eviction callback.
*/
@SuppressWarnings("unchecked")
public GridUnsafePartitionedMap(int parts, int concurrency, float load, long initCap, long totalMem,
short lruStripes, @Nullable GridOffHeapEvictListener evictLsnr) {
this.parts = parts;
this.concurrency = concurrency;
this.load = load;
// Unchecked assignment to avoid generic array creation.
partMap = new GridUnsafeMap[parts];
mem = new GridUnsafeMemory(totalMem);
if (totalMem > 0)
this.evictLsnr = evictLsnr;
lru = totalMem > 0 ? new GridUnsafeLru(lruStripes, mem) : null;
long cnt = initCap / parts;
int mod = (int)(initCap % parts);
// Since we have natural striping provided by partitioning, we can proportionally reduce
// number of segments in each map.
concurrency /= parts;
if (concurrency < MIN_SEGMENT_CONCURRENCY)
concurrency = MIN_SEGMENT_CONCURRENCY;
for (int p = 0; p < parts; p++) {
mod--;
long init = mod >= 0 ? cnt + 1 : cnt;
partMap[p] = new GridUnsafeMap(p, concurrency, load, init, totalCnt, mem, lru, evictLsnr,
new GridUnsafeLruPoller() {
@Override public void lruPoll(int size) {
if (lru == null)
return;
int left = size;
while (left > 0) {
// Pre-poll outside of lock.
long qAddr = lru.prePoll();
if (qAddr == 0)
return; // LRU is empty.
short order = lru.order(qAddr);
int part = lru.partition(order, qAddr);
int released = partMap[part].freeSpace(order, qAddr);
if (released == 0)
return;
left -= released;
}
}
}
);
}
}
/** {@inheritDoc} */
@Override public float loadFactor() {
return load;
}
/** {@inheritDoc} */
@Override public int concurrency() {
return concurrency;
}
/** {@inheritDoc} */
@Override public int partitions() {
return parts;
}
/**
* @param p Partition.
* @return Map for partition.
*/
private GridOffHeapMap mapFor(int p) {
assert p < parts;
return partMap[p];
}
/** {@inheritDoc} */
@Override public boolean contains(int part, int hash, byte[] keyBytes) {
return mapFor(part).contains(hash, keyBytes);
}
/** {@inheritDoc} */
@Override public byte[] get(int p, int hash, byte[] keyBytes) {
return mapFor(p).get(hash, keyBytes);
}
/** {@inheritDoc} */
@Override public IgniteBiTuple<Long, Integer> valuePointer(int p, int hash, byte[] keyBytes) {
return mapFor(p).valuePointer(hash, keyBytes);
}
/** {@inheritDoc} */
@Override public void enableEviction(int p, int hash, byte[] keyBytes) {
if (lru == null)
return;
mapFor(p).enableEviction(hash, keyBytes);
}
/** {@inheritDoc} */
@Override public byte[] remove(int p, int hash, byte[] keyBytes) {
return mapFor(p).remove(hash, keyBytes);
}
/** {@inheritDoc} */
@Override public boolean removex(int p, int hash, byte[] keyBytes) {
return mapFor(p).removex(hash, keyBytes);
}
/** {@inheritDoc} */
@Override public boolean removex(int part,
int hash,
byte[] keyBytes,
IgniteBiPredicate<Long, Integer> p) {
return mapFor(part).removex(hash, keyBytes, p);
}
/** {@inheritDoc} */
@Override public boolean put(int p, int hash, byte[] keyBytes, byte[] valBytes) {
return mapFor(p).put(hash, keyBytes, valBytes);
}
/** {@inheritDoc} */
@Override public void insert(int p, int hash, byte[] keyBytes, byte[] valBytes) {
mapFor(p).insert(hash, keyBytes, valBytes);
}
/** {@inheritDoc} */
@Override public long size() {
return totalCnt.sum();
}
/** {@inheritDoc} */
@Override public long size(Set<Integer> parts) {
int cnt = 0;
for (Integer part : parts)
cnt += mapFor(part).size();
return cnt;
}
/** {@inheritDoc} */
@Override public long memorySize() {
return mem.totalSize();
}
/** {@inheritDoc} */
@Override public long allocatedSize() {
return mem.allocatedSize();
}
/** {@inheritDoc} */
@Override public long systemAllocatedSize() {
return mem.systemAllocatedSize();
}
/** {@inheritDoc} */
@Override public long freeSize() {
return mem.freeSize();
}
/** {@inheritDoc} */
@Override public boolean eventListener(GridOffHeapEventListener evtLsnr) {
if (this.evtLsnr != null)
return false;
this.evtLsnr = evtLsnr;
for (GridUnsafeMap m : partMap)
m.eventListener(evtLsnr);
mem.listen(evtLsnr);
return true;
}
/** {@inheritDoc} */
@Override public boolean evictListener(GridOffHeapEvictListener evictLsnr) {
if (this.evictLsnr != null)
return false;
this.evictLsnr = evictLsnr;
for (GridUnsafeMap m : partMap)
m.evictListener(evictLsnr);
return true;
}
/** {@inheritDoc} */
@Override public void destruct() {
for (GridUnsafeMap m : partMap)
m.destruct();
if (lru != null)
lru.destruct();
}
/** {@inheritDoc} */
@Override public GridCloseableIterator<IgniteBiTuple<byte[], byte[]>> iterator() {
return new PartitionedMapCloseableIterator<IgniteBiTuple<byte[], byte[]>>() {
protected void advance() throws IgniteCheckedException {
curIt = null;
while (p < parts) {
curIt = mapFor(p++).iterator();
if (curIt.hasNext())
return;
else
curIt.close();
}
curIt = null;
}
};
}
/** {@inheritDoc} */
@Override public <T> GridCloseableIterator<T> iterator(final CX2<T2<Long, Integer>, T2<Long, Integer>, T> c) {
assert c != null;
return new PartitionedMapCloseableIterator<T>() {
protected void advance() throws IgniteCheckedException {
curIt = null;
while (p < parts) {
curIt = mapFor(p++).iterator(c);
if (curIt.hasNext())
return;
else
curIt.close();
}
curIt = null;
}
};
}
/** {@inheritDoc} */
@Override public <T> GridCloseableIterator<T> iterator(final CX2<T2<Long, Integer>, T2<Long, Integer>, T> c,
int part) {
return mapFor(part).iterator(c);
}
/** {@inheritDoc} */
@Override public GridCloseableIterator<IgniteBiTuple<byte[], byte[]>> iterator(int p) {
return mapFor(p).iterator();
}
/**
* Gets number of LRU stripes.
*
* @return Number of LRU stripes.
*/
public short lruStripes() {
return lru.concurrency();
}
/**
* Gets memory size occupied by LRU queue.
*
* @return Memory size occupied by LRU queue.
*/
public long lruMemorySize() {
return lru.memorySize();
}
/**
* Gets number of elements in LRU queue.
*
* @return Number of elements in LRU queue.
*/
public long lruSize() {
return lru.size();
}
/**
* Partitioned closable iterator.
*/
private abstract class PartitionedMapCloseableIterator<T> extends GridCloseableIteratorAdapter<T> {
/** Current partition. */
protected int p;
/** Current iterator. */
protected GridCloseableIterator<T> curIt;
{
try {
advance();
}
catch (IgniteCheckedException e) {
e.printStackTrace(); // Should never happen.
}
}
/**
* Switch to next partition.
*
* @throws IgniteCheckedException If failed.
*/
abstract void advance() throws IgniteCheckedException;
/** {@inheritDoc} */
@Override protected T onNext() throws IgniteCheckedException {
if (curIt == null)
throw new NoSuchElementException();
T t = curIt.next();
if (!curIt.hasNext()) {
curIt.close();
advance();
}
return t;
}
/** {@inheritDoc} */
@Override protected boolean onHasNext() {
return curIt != null;
}
/** {@inheritDoc} */
@Override protected void onRemove() {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override protected void onClose() throws IgniteCheckedException {
if (curIt != null)
curIt.close();
}
}
}