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

org.xnio.ByteBufferSlicePool Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 32.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2010 Red Hat, Inc. and/or its affiliates, and individual
 * contributors as indicated by the @author tags.
 *
 * 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.
 */

package org.xnio;

import java.nio.ByteBuffer;
import java.security.AccessController;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import static org.xnio._private.Messages.msg;

/**
 * A buffer pooled allocator.  This pool uses a series of buffer regions to back the
 * returned pooled buffers.  When the buffer is no longer needed, it should be freed back into the pool; failure
 * to do so will cause the corresponding buffer area to be unavailable until the buffer is garbage-collected.
 *
 * @author David M. Lloyd
 */
public final class ByteBufferSlicePool implements Pool {

    private static final int LOCAL_LENGTH;

    static {
        String value = AccessController.doPrivileged(new ReadPropertyAction("xnio.bufferpool.threadlocal.size", "12"));
        int val;
        try {
            val = Integer.parseInt(value);
        } catch (NumberFormatException ignored) {
            val = 12;
        }
        LOCAL_LENGTH = val;
    }

    private final Set refSet = Collections.synchronizedSet(new HashSet());
    private final Queue sliceQueue;
    private final BufferAllocator allocator;
    private final int bufferSize;
    private final int buffersPerRegion;
    private final int threadLocalQueueSize;
    private final ThreadLocal localQueueHolder = new ThreadLocal() {
        protected ThreadLocalCache initialValue() {
            //noinspection serial
            return new ThreadLocalCache(this);
        }

        public void remove() {
            final ArrayDeque deque = get().queue;
            Slice slice = deque.poll();
            while (slice != null) {
                doFree(slice);
                slice = deque.poll();
            }
            super.remove();
        }
    };

    /**
     * Construct a new instance.
     *
     * @param allocator the buffer allocator to use
     * @param bufferSize the size of each buffer
     * @param maxRegionSize the maximum region size for each backing buffer
     * @param threadLocalQueueSize the number of buffers to cache on each thread
     */
    public ByteBufferSlicePool(final BufferAllocator allocator, final int bufferSize, final int maxRegionSize, final int threadLocalQueueSize) {
        if (bufferSize <= 0) {
            throw msg.parameterOutOfRange("bufferSize");
        }
        if (maxRegionSize < bufferSize) {
            throw msg.parameterOutOfRange("bufferSize");
        }
        buffersPerRegion = maxRegionSize / bufferSize;
        this.bufferSize = bufferSize;
        this.allocator = allocator;
        sliceQueue = new ConcurrentLinkedQueue();
        this.threadLocalQueueSize = threadLocalQueueSize;
    }

    /**
     * Construct a new instance.
     *
     * @param allocator the buffer allocator to use
     * @param bufferSize the size of each buffer
     * @param maxRegionSize the maximum region size for each backing buffer
     */
    public ByteBufferSlicePool(final BufferAllocator allocator, final int bufferSize, final int maxRegionSize) {
        this(allocator, bufferSize, maxRegionSize, LOCAL_LENGTH);
    }

    /**
     * Construct a new instance, using a direct buffer allocator.
     *
     * @param bufferSize the size of each buffer
     * @param maxRegionSize the maximum region size for each backing buffer
     */
    public ByteBufferSlicePool(final int bufferSize, final int maxRegionSize) {
        this(BufferAllocator.DIRECT_BYTE_BUFFER_ALLOCATOR, bufferSize, maxRegionSize);
    }

    /** {@inheritDoc} */
    public Pooled allocate() {
        ThreadLocalCache localCache = localQueueHolder.get();
        if(localCache.outstanding != threadLocalQueueSize) {
            localCache.outstanding++;
        }
        Slice slice = localCache.queue.poll();
        if (slice != null) {
            return new PooledByteBuffer(slice, slice.slice());
        }
        final Queue sliceQueue = this.sliceQueue;
        slice = sliceQueue.poll();
        if (slice != null) {
            return new PooledByteBuffer(slice, slice.slice());
        }
        synchronized (sliceQueue) {
            slice = sliceQueue.poll();
            if (slice != null) {
                return new PooledByteBuffer(slice, slice.slice());
            }
            final int bufferSize = this.bufferSize;
            final int buffersPerRegion = this.buffersPerRegion;
            final ByteBuffer region = allocator.allocate(buffersPerRegion * bufferSize);
            int idx = bufferSize;
            for (int i = 1; i < buffersPerRegion; i ++) {
                sliceQueue.add(new Slice(region, idx, bufferSize));
                idx += bufferSize;
            }
            final Slice newSlice = new Slice(region, 0, bufferSize);
            return new PooledByteBuffer(newSlice, newSlice.slice());
        }
    }

    /**
     * Return the size of the {@link ByteBuffer}s that are returned by {@link #allocate()}.
     */
    public int getBufferSize() {
        return bufferSize;
    }

    private void doFree(Slice region) {
        final ThreadLocalCache localCache = localQueueHolder.get();
        boolean cacheOk = false;
        if(localCache.outstanding > 0) {
            localCache.outstanding--;
            cacheOk = true;
        }
        ArrayDeque localQueue = localCache.queue;
        if (localQueue.size() == threadLocalQueueSize || !cacheOk) {
            sliceQueue.add(region);
        } else {
            localQueue.add(region);
        }
    }

    private final class PooledByteBuffer implements Pooled {
        private final Slice region;
        ByteBuffer buffer;

        PooledByteBuffer(final Slice region, final ByteBuffer buffer) {
            this.region = region;
            this.buffer = buffer;
        }

        public void discard() {
            final ByteBuffer buffer = this.buffer;
            this.buffer = null;
            if (buffer != null) {
                // free when GC'd, no sooner
                refSet.add(new Ref(buffer, region));
            }
        }

        public void free() {
            ByteBuffer buffer = this.buffer;
            this.buffer = null;
            if (buffer != null) {
                // trust the user, repool the buffer
                doFree(region);
            }
        }

        public ByteBuffer getResource() {
            final ByteBuffer buffer = this.buffer;
            if (buffer == null) {
                throw msg.bufferFreed();
            }
            return buffer;
        }

        public void close() {
            free();
        }

        public String toString() {
            return "Pooled buffer " + buffer;
        }
    }

    private final class Slice {
        private final ByteBuffer parent;

        private Slice(final ByteBuffer parent, final int start, final int size) {
            this.parent = (ByteBuffer)parent.duplicate().position(start).limit(start+size);
        }

        ByteBuffer slice() {
            return parent.slice();
        }
    }

    final class Ref extends AutomaticReference {
        private final Slice region;

        private Ref(final ByteBuffer referent, final Slice region) {
            super(referent, AutomaticReference.PERMIT);
            this.region = region;
        }

        protected void free() {
            doFree(region);
            refSet.remove(this);
        }
    }

    private final class ThreadLocalCache {

        private final ThreadLocal threadLocal;

        final ArrayDeque queue =  new ArrayDeque(threadLocalQueueSize) {

            /**
             * This sucks but there's no other way to ensure these buffers are returned to the pool.
             */
            protected void finalize() {
                threadLocal.remove();
            }
        };

        int outstanding = 0;

        private ThreadLocalCache(ThreadLocal threadLocal) {
            this.threadLocal = threadLocal;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy