All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.officefloor.server.stream.impl.ThreadLocalStreamBufferPool Maven / Gradle / Ivy

The newest version!
/*-
 * #%L
 * HTTP Server
 * %%
 * Copyright (C) 2005 - 2020 Daniel Sagenschneider
 * %%
 * 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.
 * #L%
 */

package net.officefloor.server.stream.impl;

import java.nio.ByteBuffer;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;

import net.officefloor.frame.api.managedobject.pool.ManagedObjectPool;
import net.officefloor.frame.api.managedobject.pool.ThreadCompletionListener;
import net.officefloor.frame.api.managedobject.pool.ThreadCompletionListenerFactory;
import net.officefloor.server.stream.BufferJvmFix;
import net.officefloor.server.stream.ByteBufferFactory;
import net.officefloor.server.stream.StreamBuffer;
import net.officefloor.server.stream.StreamBufferPool;

/**
 * {@link StreamBufferPool} of {@link ByteBuffer} instances that utilises
 * {@link ThreadLocal} caches for performance.
 * 
 * @author Daniel Sagenschneider
 */
public class ThreadLocalStreamBufferPool extends AbstractStreamBufferPool
		implements ThreadCompletionListenerFactory, ThreadCompletionListener {

	/**
	 * {@link ThreadLocalPool}.
	 */
	private final ThreadLocal threadLocalPool = new ThreadLocal<>();

	/**
	 * Number of {@link StreamBuffer} instances in circulation.
	 */
	private final AtomicInteger bufferCount = new AtomicInteger(0);

	/**
	 * {@link ByteBufferFactory}.
	 */
	private final ByteBufferFactory byteBufferFactory;

	/**
	 * Maximum {@link ThreadLocal} pool size.
	 */
	private final int maxThreadLocalPoolSize;

	/**
	 * Maximum core pool size.
	 */
	private volatile int maxCorePoolSize;

	/**
	 * Core pool of {@link StreamBuffer} instances.
	 */
	private Deque> corePool = new ConcurrentLinkedDeque<>();

	/**
	 * 

* Instantiate with details of pool sizes. *

* The total potential amount of memory used is: *

* pooledByteBufferSize * (active buffers + * (threadLocalPoolSize * active threads) + * corePoolSize). * * @param byteBufferFactory {@link ByteBufferFactory}. * @param maxThreadLocalPoolSize Maximum {@link ThreadLocal} pool size. * @param maxCorePoolSize Maximum core pool size. */ public ThreadLocalStreamBufferPool(ByteBufferFactory byteBufferFactory, int maxThreadLocalPoolSize, int maxCorePoolSize) { this.byteBufferFactory = byteBufferFactory; this.maxThreadLocalPoolSize = maxThreadLocalPoolSize; this.maxCorePoolSize = maxCorePoolSize; } /** * Activates {@link ThreadLocal} pooling of {@link StreamBuffer} on current * {@link Thread}. */ public void activeThreadLocalPooling() { // Ensure only singleton on the thread if (this.threadLocalPool.get() == null) { this.threadLocalPool.set(new ThreadLocalPool()); } } /** * Obtains the number of {@link StreamBuffer} instances in circulation. * * @return Number of {@link StreamBuffer} instances in circulation. */ public int getStreamBufferCount() { return this.bufferCount.get(); } /** * Releases the {@link StreamBuffer} to the core pool. * * @param buffer {@link StreamBuffer}. */ private void releaseToCorePool(StreamBuffer buffer) { // Determine if release (keep approximate core pool size) if (this.corePool.size() < this.maxCorePoolSize) { // Released to core pool this.corePool.push(buffer); return; } // Allow buffer to be garbage collected (too many buffers) this.bufferCount.decrementAndGet(); } /** * Creates a pooled {@link StreamBuffer}. * * @return New pooled {@link StreamBuffer}. */ private StreamBuffer createPooledStreamBuffer() { // Create and return new buffer ByteBuffer byteBuffer = this.byteBufferFactory.createByteBuffer(); StreamBuffer streamBuffer = new PooledStreamBuffer(byteBuffer); // Capture created buffer this.bufferCount.incrementAndGet(); // Return the created buffer return streamBuffer; } /** * =============== StreamBufferPool =========================== */ @Override public StreamBuffer getPooledStreamBuffer() { // Attempt to obtain from thread local pool ThreadLocalPool pool = threadLocalPool.get(); if (pool != null) { if (pool.threadHead != null) { // Obtain from thread pool StreamBuffer pooledBuffer = pool.threadHead; pool.threadHead = pool.threadHead.next; pool.threadPoolSize--; // Clear buffer, so reset for use BufferJvmFix.clear(pooledBuffer.pooledBuffer); pooledBuffer.next = null; // Use the thread local buffer return pooledBuffer; } } // No thread buffers, so attempt core pool StreamBuffer pooledBuffer = this.corePool.poll(); if (pooledBuffer != null) { // Clear buffer, so reset for use BufferJvmFix.clear(pooledBuffer.pooledBuffer); pooledBuffer.next = null; // Use the core pool buffer return pooledBuffer; } // Create new buffer return this.createPooledStreamBuffer(); } @Override public void close() { // Thread local pools clean on thread exit, so avoid going to core this.maxCorePoolSize = 0; // Release reference to allow GC of core pooled buffers this.corePool.clear(); } /** * ============= ThreadCompletionListenerFactory ========= */ @Override public ThreadCompletionListener createThreadCompletionListener(ManagedObjectPool pool) { return this; } /** * ================= ThreadCompletionListener ============ */ @Override public void threadComplete() { // Obtain the thread pool ThreadLocalPool pool = this.threadLocalPool.get(); if (pool == null) { return; // no thread local pool to clean up } // Release all to pool StreamBuffer buffer = pool.threadHead; while (buffer != null) { // Obtain release buffer (must obtain next, as release sets next) StreamBuffer release = buffer; buffer = buffer.next; // Release the buffer this.releaseToCorePool(release); } // Remove from thread local pooling this.threadLocalPool.remove(); } /** * Pooled {@link StreamBuffer}. */ private class PooledStreamBuffer extends StreamBuffer { /** * Instantiate. * * @param byteBuffer {@link ByteBuffer}. */ private PooledStreamBuffer(ByteBuffer byteBuffer) { super(byteBuffer, null, null); } /* * ================= StreamBuffer ======================== */ @Override public boolean write(byte datum) { // Ensure space write data if (this.pooledBuffer.remaining() <= 0) { return false; // buffer is full } // Write the data this.pooledBuffer.put(datum); return true; } @Override public int write(byte[] data, int offset, int length) { // Obtain the bytes to write int writeBytes = Math.min(length, this.pooledBuffer.remaining()); // Write the bytes this.pooledBuffer.put(data, offset, writeBytes); // Return the number of written bytes return writeBytes; } @Override public void release() { // Easy access to pool @SuppressWarnings("resource") ThreadLocalStreamBufferPool bufferPool = ThreadLocalStreamBufferPool.this; // Attempt to release to thread local pool ThreadLocalPool pool = bufferPool.threadLocalPool.get(); if (pool != null) { if (pool.threadPoolSize < bufferPool.maxThreadLocalPoolSize) { // Release to thread pool this.next = pool.threadHead; pool.threadHead = this; pool.threadPoolSize++; return; // released } } // As here, release to core pool bufferPool.releaseToCorePool(this); } } /** * {@link Thread} local pool of {@link StreamBuffer} instances. */ private static class ThreadLocalPool { /** * Head {@link StreamBuffer} to linked list of {@link StreamBuffer} instances. */ private StreamBuffer threadHead = null; /** * Number of {@link StreamBuffer} instances within this pool. */ private int threadPoolSize = 0; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy