/** * 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.hadoop.hive.llap.cache; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.hive.common.io.encoded.MemoryBuffer; import org.apache.hadoop.hive.llap.DebugUtils; import org.apache.hadoop.hive.llap.io.api.impl.LlapIoImpl; import com.google.common.annotations.VisibleForTesting; public final class LlapDataBuffer extends LlapCacheableBuffer implements MemoryBuffer { // For now, we don't track refcount for metadata blocks, don't clear them, don't reuse them and // basically rely on GC to remove them. So, refcount only applies to data blocks. If that // changes, refcount management should be move to LlapCacheableBuffer to be shared. private static final int EVICTED_REFCOUNT = -1; public static final int UNKNOWN_CACHED_LENGTH = -1; protected final AtomicInteger refCount = new AtomicInteger(0); public ByteBuffer byteBuffer; /** Allocator uses this to remember which arena to alloc from. */ public int arenaIndex = -1; /** Allocator uses this to remember the allocation size. */ public int allocSize; /** ORC cache uses this to store compressed length; buffer is cached uncompressed, but * the lookup is on compressed ranges, so we need to know this. */ public int declaredCachedLength = UNKNOWN_CACHED_LENGTH; public void initialize( int arenaIndex, ByteBuffer byteBuffer, int offset, int length) { this.byteBuffer = byteBuffer.slice(); this.byteBuffer.position(offset); this.byteBuffer.limit(offset + length); this.arenaIndex = arenaIndex; this.allocSize = length; } @Override public ByteBuffer getByteBufferDup() { return byteBuffer.duplicate(); } @Override public ByteBuffer getByteBufferRaw() { return byteBuffer; } @Override public long getMemoryUsage() { return allocSize; } @Override public void notifyEvicted(EvictionDispatcher evictionDispatcher) { evictionDispatcher.notifyEvicted(this); } int incRef() { int newRefCount = -1; while (true) { int oldRefCount = refCount.get(); if (oldRefCount == EVICTED_REFCOUNT) return -1; assert oldRefCount >= 0 : "oldRefCount is " + oldRefCount + " " + this; newRefCount = oldRefCount + 1; if (refCount.compareAndSet(oldRefCount, newRefCount)) break; } if (LlapIoImpl.LOCKING_LOGGER.isTraceEnabled()) { LlapIoImpl.LOCKING_LOGGER.trace("Locked {}; new ref count {}", this, newRefCount); } return newRefCount; } @VisibleForTesting int getRefCount() { return refCount.get(); } @VisibleForTesting @Override public boolean isLocked() { // Best-effort check. We cannot do a good check against caller thread, since // refCount could still be > 0 if someone else locked. This is used for asserts and logs. return refCount.get() > 0; } @VisibleForTesting public boolean isInvalid() { return refCount.get() == EVICTED_REFCOUNT; } int decRef() { int newRefCount = refCount.decrementAndGet(); if (LlapIoImpl.LOCKING_LOGGER.isTraceEnabled()) { LlapIoImpl.LOCKING_LOGGER.trace("Unlocked {}; refcount {}", this, newRefCount); } if (newRefCount < 0) { throw new AssertionError("Unexpected refCount " + newRefCount + ": " + this); } return newRefCount; } /** * @return Whether the we can invalidate; false if locked or already evicted. */ @Override public boolean invalidate() { while (true) { int value = refCount.get(); if (value != 0) return false; if (refCount.compareAndSet(value, EVICTED_REFCOUNT)) break; } if (LlapIoImpl.LOCKING_LOGGER.isTraceEnabled()) { LlapIoImpl.LOCKING_LOGGER.trace("Invalidated {} due to eviction", this); } return true; } @Override public String toString() { int refCount = this.refCount.get(); return "0x" + Integer.toHexString(System.identityHashCode(this)) + "(" + refCount + ")"; } public static String toDataString(MemoryBuffer s) { if (s == null || s.getByteBufferRaw().remaining() == 0) return "" + s; byte b = s.getByteBufferRaw().get(s.getByteBufferRaw().position()); int i = (b < 0) ? -b : b; return s + " (0x" + Integer.toHexString(i) + ")"; } }