/*
* 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.geode.internal.tcp;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.geode.distributed.internal.DMStats;
import org.apache.geode.internal.Assert;
/**
*
*/
public class Buffers {
/**
* A list of soft references to byte buffers.
*/
private static final ConcurrentLinkedQueue bufferQueue = new ConcurrentLinkedQueue();
/**
* Should only be called by threads that have currently acquired send permission.
*
* @return a byte buffer to be used for sending on this connection.
*/
static ByteBuffer acquireSenderBuffer(int size, DMStats stats) {
return acquireBuffer(size, stats, true);
}
static ByteBuffer acquireReceiveBuffer(int size, DMStats stats) {
return acquireBuffer(size, stats, false);
}
static ByteBuffer acquireBuffer(int size, DMStats stats, boolean send) {
ByteBuffer result;
if (TCPConduit.useDirectBuffers) {
IdentityHashMap<BBSoftReference, BBSoftReference> alreadySeen = null; // keys are used like a
// set
BBSoftReference ref = (BBSoftReference) bufferQueue.poll();
while (ref != null) {
ByteBuffer bb = ref.getBB();
if (bb == null) {
// it was garbage collected
int refSize = ref.consumeSize();
if (refSize > 0) {
if (ref.getSend()) { // fix bug 46773
stats.incSenderBufferSize(-refSize, true);
} else {
stats.incReceiverBufferSize(-refSize, true);
}
}
} else if (bb.capacity() >= size) {
bb.rewind();
bb.limit(size);
return bb;
} else {
// wasn't big enough so put it back in the queue
Assert.assertTrue(bufferQueue.offer(ref));
if (alreadySeen == null) {
alreadySeen = new IdentityHashMap<BBSoftReference, BBSoftReference>();
}
if (alreadySeen.put(ref, ref) != null) {
// if it returns non-null then we have already seen this item
// so we have worked all the way through the queue once.
// So it is time to give up and allocate a new buffer.
break;
}
}
ref = (BBSoftReference) bufferQueue.poll();
}
result = ByteBuffer.allocateDirect(size);
} else {
// if we are using heap buffers then don't bother with keeping them around
result = ByteBuffer.allocate(size);
}
if (send) {
stats.incSenderBufferSize(size, TCPConduit.useDirectBuffers);
} else {
stats.incReceiverBufferSize(size, TCPConduit.useDirectBuffers);
}
return result;
}
static void releaseSenderBuffer(ByteBuffer bb, DMStats stats) {
releaseBuffer(bb, stats, true);
}
static void releaseReceiveBuffer(ByteBuffer bb, DMStats stats) {
releaseBuffer(bb, stats, false);
}
/**
* Releases a previously acquired buffer.
*/
static void releaseBuffer(ByteBuffer bb, DMStats stats, boolean send) {
if (TCPConduit.useDirectBuffers) {
BBSoftReference bbRef = new BBSoftReference(bb, send);
bufferQueue.offer(bbRef);
} else {
if (send) {
stats.incSenderBufferSize(-bb.capacity(), false);
} else {
stats.incReceiverBufferSize(-bb.capacity(), false);
}
}
}
public static void initBufferStats(DMStats stats) { // fixes 46773
if (TCPConduit.useDirectBuffers) {
@SuppressWarnings("unchecked")
Iterator<BBSoftReference> it = (Iterator<BBSoftReference>) bufferQueue.iterator();
while (it.hasNext()) {
BBSoftReference ref = it.next();
if (ref.getBB() != null) {
if (ref.getSend()) { // fix bug 46773
stats.incSenderBufferSize(ref.getSize(), true);
} else {
stats.incReceiverBufferSize(ref.getSize(), true);
}
}
}
}
}
/**
* A soft reference that remembers the size of the byte buffer it refers to. TODO Dan - I really
* think this should be a weak reference. The JVM doesn't seem to clear soft references if it is
* getting low on direct memory.
*/
private static class BBSoftReference extends SoftReference<ByteBuffer> {
private int size;
private final boolean send;
public BBSoftReference(ByteBuffer bb, boolean send) {
super(bb);
this.size = bb.capacity();
this.send = send;
}
public int getSize() {
return this.size;
}
public synchronized int consumeSize() {
int result = this.size;
this.size = 0;
return result;
}
public boolean getSend() {
return this.send;
}
public ByteBuffer getBB() {
return (ByteBuffer) super.get();
}
}
}