/* * Copyright 2012 The Netty Project * * The Netty Project 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 io.netty_voltpatches.buffer; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import io.netty_voltpatches.util.internal.StringUtil; import static java.lang.Math.*; final class PoolChunkList<T> implements PoolChunkListMetric { private static final Iterator<PoolChunkMetric> EMPTY_METRICS = Collections.<PoolChunkMetric>emptyList().iterator(); private final PoolChunkList<T> nextList; private final int minUsage; private final int maxUsage; private final int maxCapacity; private PoolChunk<T> head; // This is only update once when create the linked like list of PoolChunkList in PoolArena constructor. private PoolChunkList<T> prevList; // TODO: Test if adding padding helps under contention //private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; PoolChunkList(PoolChunkList<T> nextList, int minUsage, int maxUsage, int chunkSize) { assert minUsage <= maxUsage; this.nextList = nextList; this.minUsage = minUsage; this.maxUsage = maxUsage; maxCapacity = calculateMaxCapacity(minUsage, chunkSize); } /** * Calculates the maximum capacity of a buffer that will ever be possible to allocate out of the {@link PoolChunk}s * that belong to the {@link PoolChunkList} with the given {@code minUsage} and {@code maxUsage} settings. */ private static int calculateMaxCapacity(int minUsage, int chunkSize) { minUsage = minUsage0(minUsage); if (minUsage == 100) { // If the minUsage is 100 we can not allocate anything out of this list. return 0; } // Calculate the maximum amount of bytes that can be allocated from a PoolChunk in this PoolChunkList. // // As an example: // - If a PoolChunkList has minUsage == 25 we are allowed to allocate at most 75% of the chunkSize because // this is the maximum amount available in any PoolChunk in this PoolChunkList. return (int) (chunkSize * (100L - minUsage) / 100L); } void prevList(PoolChunkList<T> prevList) { assert this.prevList == null; this.prevList = prevList; } boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) { if (head == null || normCapacity > maxCapacity) { // Either this PoolChunkList is empty or the requested capacity is larger then the capacity which can // be handled by the PoolChunks that are contained in this PoolChunkList. return false; } for (PoolChunk<T> cur = head;;) { long handle = cur.allocate(normCapacity); if (handle < 0) { cur = cur.next; if (cur == null) { return false; } } else { cur.initBuf(buf, handle, reqCapacity); if (cur.usage() >= maxUsage) { remove(cur); nextList.add(cur); } return true; } } } boolean free(PoolChunk<T> chunk, long handle) { chunk.free(handle); if (chunk.usage() < minUsage) { remove(chunk); // Move the PoolChunk down the PoolChunkList linked-list. return move0(chunk); } return true; } private boolean move(PoolChunk<T> chunk) { assert chunk.usage() < maxUsage; if (chunk.usage() < minUsage) { // Move the PoolChunk down the PoolChunkList linked-list. return move0(chunk); } // PoolChunk fits into this PoolChunkList, adding it here. add0(chunk); return true; } /** * Moves the {@link PoolChunk} down the {@link PoolChunkList} linked-list so it will end up in the right * {@link PoolChunkList} that has the correct minUsage / maxUsage in respect to {@link PoolChunk#usage()}. */ private boolean move0(PoolChunk<T> chunk) { if (prevList == null) { // There is no previous PoolChunkList so return false which result in having the PoolChunk destroyed and // all memory associated with the PoolChunk will be released. assert chunk.usage() == 0; return false; } return prevList.move(chunk); } void add(PoolChunk<T> chunk) { if (chunk.usage() >= maxUsage) { nextList.add(chunk); return; } add0(chunk); } /** * Adds the {@link PoolChunk} to this {@link PoolChunkList}. */ void add0(PoolChunk<T> chunk) { chunk.parent = this; if (head == null) { head = chunk; chunk.prev = null; chunk.next = null; } else { chunk.prev = null; chunk.next = head; head.prev = chunk; head = chunk; } } private void remove(PoolChunk<T> cur) { if (cur == head) { head = cur.next; if (head != null) { head.prev = null; } } else { PoolChunk<T> next = cur.next; cur.prev.next = next; if (next != null) { next.prev = cur.prev; } } } @Override public int minUsage() { return minUsage0(minUsage); } @Override public int maxUsage() { return min(maxUsage, 100); } private static int minUsage0(int value) { return max(1, value); } @Override public Iterator<PoolChunkMetric> iterator() { if (head == null) { return EMPTY_METRICS; } List<PoolChunkMetric> metrics = new ArrayList<PoolChunkMetric>(); for (PoolChunk<T> cur = head;;) { metrics.add(cur); cur = cur.next; if (cur == null) { break; } } return metrics.iterator(); } @Override public String toString() { if (head == null) { return "none"; } StringBuilder buf = new StringBuilder(); for (PoolChunk<T> cur = head;;) { buf.append(cur); cur = cur.next; if (cur == null) { break; } buf.append(StringUtil.NEWLINE); } return buf.toString(); } void destroy(PoolArena<T> arena) { PoolChunk<T> chunk = head; while (chunk != null) { arena.destroyChunk(chunk); chunk = chunk.next; } head = null; } }