/*
* 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.query.h2.opt;
import java.util.concurrent.locks.Lock;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.GridStripedLock;
import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.h2.store.Data;
import org.h2.value.Value;
import org.jetbrains.annotations.Nullable;
/**
* Offheap row.
*/
public class GridH2KeyValueRowOffheap extends GridH2AbstractKeyValueRow {
/** */
private static final GridStripedLock lock;
/**
* Init locks.
*/
static {
int cpus = Runtime.getRuntime().availableProcessors();
lock = new GridStripedLock(cpus * cpus * 8);
}
/** */
private static final int OFFSET_KEY_SIZE = 4; // 4 after ref cnt int
/** */
private static final int OFFSET_VALUE_REF = OFFSET_KEY_SIZE + 4; // 8
/** */
private static final int OFFSET_EXPIRATION = OFFSET_VALUE_REF + 8; // 16
/** */
private static final int OFFSET_KEY = OFFSET_EXPIRATION + 8; // 24
/** */
private static final int OFFSET_VALUE = 4; // 4 on separate page after val size int
/** */
private static final Data SIZE_CALCULATOR = Data.create(null, null);
/** */
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
private long ptr;
/**
* @param desc Row descriptor.
* @param ptr Pointer.
*/
public GridH2KeyValueRowOffheap(GridH2RowDescriptor desc, long ptr) {
super(desc);
assert ptr > 0 : ptr;
this.ptr = ptr;
}
/**
* Constructor.
*
* @param desc Row descriptor.
* @param key Key.
* @param keyType Key type.
* @param val Value.
* @param valType Value type.
* @param ver Version.
* @param expirationTime Expiration time.
* @throws IgniteCheckedException If failed.
*/
public GridH2KeyValueRowOffheap(GridH2RowDescriptor desc, Object key, int keyType, @Nullable Object val, int valType,
GridCacheVersion ver, long expirationTime) throws IgniteCheckedException {
super(desc, key, keyType, val, valType, ver, expirationTime);
}
/** {@inheritDoc} */
@Override public long expireTime() {
if (expirationTime == 0) {
long p = ptr;
assert p > 0 : p;
// We don't need any synchronization or volatility here because we publish via
// volatile write to tree node.
expirationTime = desc.memory().readLong(p + OFFSET_EXPIRATION);
}
return expirationTime;
}
/** {@inheritDoc} */
@Override protected void cache() {
desc.cache(this);
}
/**
* @param ptr Pointer to get lock for.
* @return Locked lock, must be released in {@code finally} block.
*/
@SuppressWarnings("LockAcquiredButNotSafelyReleased")
private static Lock lock(long ptr) {
assert ptr > 0 : ptr;
assert (ptr & 7) == 0 : ptr; // Unsafe allocated pointers aligned.
Lock l = lock.getLock(ptr >>> 3);
l.lock();
return l;
}
/** {@inheritDoc} */
@SuppressWarnings("LockAcquiredButNotSafelyReleased")
@Override protected Value getOffheapValue(int col) {
GridUnsafeMemory mem = desc.memory();
long p = ptr;
assert p > 0 : p;
byte[] bytes = null;
if (col == KEY_COL) {
int size = mem.readInt(p + OFFSET_KEY_SIZE);
assert size > 0 : size;
bytes = mem.readBytes(p + OFFSET_KEY, size);
}
else if (col == VAL_COL) {
Lock l = lock(p);
desc.guard().begin();
try {
long valPtr = mem.readLongVolatile(p + OFFSET_VALUE_REF);
if (valPtr == 0) // Value was evicted.
return null;
int size = mem.readInt(valPtr);
assert size > 0 : size;
bytes = mem.readBytes(valPtr + OFFSET_VALUE, size);
}
finally {
desc.guard().end();
l.unlock();
}
}
else
throw new IllegalStateException("Column: " + col);
Data data = Data.create(null, bytes);
return data.readValue();
}
/** {@inheritDoc} */
@Override public long pointer() {
long p = ptr;
assert p > 0: p;
return p;
}
/** {@inheritDoc} */
@SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod")
@Override protected synchronized Value updateWeakValue(Object valObj) throws IgniteCheckedException {
Value val = peekValue(VAL_COL);
if (val != null)
return val;
Value upd = desc.wrap(valObj, desc.valueType());
setValue(VAL_COL, upd);
notifyAll();
return upd;
}
/** {@inheritDoc} */
@Override protected Value syncValue(long waitTime) {
Value v = super.syncValue(waitTime);
if (v != null)
return v;
return getOffheapValue(VAL_COL);
}
/** {@inheritDoc} */
@SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"})
@Override public void incrementRefCount() {
long p = ptr;
GridUnsafeMemory mem = desc.memory();
if (p == 0) { // Serialize data to offheap memory.
Value key = peekValue(KEY_COL);
Value val = peekValue(VAL_COL);
assert key != null;
assert val != null;
Data data = Data.create(null, new byte[SIZE_CALCULATOR.getValueLen(key)]);
data.writeValue(key);
int keySize = data.length();
p = mem.allocate(keySize + OFFSET_KEY);
// We don't need any synchronization or volatility here because we publish via
// volatile write to tree node.
mem.writeInt(p, 1);
mem.writeLong(p + OFFSET_EXPIRATION, expirationTime);
mem.writeInt(p + OFFSET_KEY_SIZE, keySize);
mem.writeBytes(p + OFFSET_KEY, data.getBytes(), 0, keySize);
data = Data.create(null, new byte[SIZE_CALCULATOR.getValueLen(val)]);
data.writeValue(val);
int valSize = data.length();
long valPtr = mem.allocate(valSize + OFFSET_VALUE);
mem.writeInt(valPtr, valSize);
mem.writeBytes(valPtr + OFFSET_VALUE, data.getBytes(), 0, valSize);
mem.writeLongVolatile(p + OFFSET_VALUE_REF, valPtr);
ptr = p;
desc.cache(this);
}
else {
for (;;) {
int cnt = mem.readIntVolatile(p);
assert cnt > 0 : cnt;
if (mem.casInt(p, cnt, cnt + 1))
break;
}
}
}
/** {@inheritDoc} */
@Override public void decrementRefCount() {
long p = ptr;
assert p > 0 : p;
GridUnsafeMemory mem = desc.memory();
for (;;) {
int cnt = mem.readIntVolatile(p);
assert cnt > 0 : cnt;
if (cnt == 1)
break;
if (mem.casInt(p, cnt, cnt - 1))
return;
}
desc.uncache(p);
// Deallocate off-heap memory.
long valPtr = mem.readLongVolatile(p + OFFSET_VALUE_REF);
assert valPtr >= 0 : valPtr;
if (valPtr != 0)
mem.release(valPtr, mem.readInt(valPtr) + OFFSET_VALUE);
mem.release(p, mem.readInt(p + OFFSET_KEY_SIZE) + OFFSET_KEY);
}
/** {@inheritDoc} */
@Override protected void addOffheapRowId(SB sb) {
sb.a('-').a(ptr);
}
/** {@inheritDoc} */
@Override public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof GridH2KeyValueRowOffheap) {
GridH2KeyValueRowOffheap row = (GridH2KeyValueRowOffheap)obj;
if (pointer() == row.pointer())
return true;
}
return false;
}
}