/* * Copyright 2014 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.util; import org.junit.Test; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.*; public class RecyclerTest { private static Recycler<HandledObject> newRecycler(int max) { return new Recycler<HandledObject>(max) { @Override protected HandledObject newObject( Recycler.Handle<HandledObject> handle) { return new HandledObject(handle); } }; } @Test(expected = IllegalStateException.class) public void testMultipleRecycle() { Recycler<HandledObject> recycler = newRecycler(1024); HandledObject object = recycler.get(); object.recycle(); object.recycle(); } @Test public void testRecycle() { Recycler<HandledObject> recycler = newRecycler(1024); HandledObject object = recycler.get(); object.recycle(); HandledObject object2 = recycler.get(); assertSame(object, object2); object2.recycle(); } @Test public void testRecycleDisable() { Recycler<HandledObject> recycler = newRecycler(-1); HandledObject object = recycler.get(); object.recycle(); HandledObject object2 = recycler.get(); assertNotSame(object, object2); object2.recycle(); } /** * Test to make sure bug #2848 never happens again * https://github.com/netty/netty/issues/2848 */ @Test public void testMaxCapacity() { testMaxCapacity(300); Random rand = new Random(); for (int i = 0; i < 50; i++) { testMaxCapacity(rand.nextInt(1000) + 256); // 256 - 1256 } } private static void testMaxCapacity(int maxCapacity) { Recycler<HandledObject> recycler = newRecycler(maxCapacity); HandledObject[] objects = new HandledObject[maxCapacity * 3]; for (int i = 0; i < objects.length; i++) { objects[i] = recycler.get(); } for (int i = 0; i < objects.length; i++) { objects[i].recycle(); objects[i] = null; } assertTrue("The threadLocalCapacity (" + recycler.threadLocalCapacity() + ") must be <= maxCapacity (" + maxCapacity + ") as we not pool all new handles internally", maxCapacity >= recycler.threadLocalCapacity()); } @Test public void testRecycleAtDifferentThread() throws Exception { final Recycler<HandledObject> recycler = new Recycler<HandledObject>(256, 10, 2, 10) { @Override protected HandledObject newObject(Recycler.Handle<HandledObject> handle) { return new HandledObject(handle); } }; final HandledObject o = recycler.get(); final HandledObject o2 = recycler.get(); final Thread thread = new Thread() { @Override public void run() { o.recycle(); o2.recycle(); } }; thread.start(); thread.join(); assertSame(recycler.get(), o); assertNotSame(recycler.get(), o2); } @Test public void testMaxCapacityWithRecycleAtDifferentThread() throws Exception { final int maxCapacity = 4; // Choose the number smaller than WeakOrderQueue.LINK_CAPACITY final Recycler<HandledObject> recycler = newRecycler(maxCapacity); // Borrow 2 * maxCapacity objects. // Return the half from the same thread. // Return the other half from the different thread. final HandledObject[] array = new HandledObject[maxCapacity * 3]; for (int i = 0; i < array.length; i ++) { array[i] = recycler.get(); } for (int i = 0; i < maxCapacity; i ++) { array[i].recycle(); } final Thread thread = new Thread() { @Override public void run() { for (int i = maxCapacity; i < array.length; i ++) { array[i].recycle(); } } }; thread.start(); thread.join(); assertEquals(maxCapacity, recycler.threadLocalCapacity()); assertEquals(1, recycler.threadLocalSize()); for (int i = 0; i < array.length; i ++) { recycler.get(); } assertEquals(maxCapacity, recycler.threadLocalCapacity()); assertEquals(0, recycler.threadLocalSize()); } @Test public void testDiscardingExceedingElementsWithRecycleAtDifferentThread() throws Exception { final int maxCapacity = 32; final AtomicInteger instancesCount = new AtomicInteger(0); final Recycler<HandledObject> recycler = new Recycler<HandledObject>(maxCapacity, 2) { @Override protected HandledObject newObject(Recycler.Handle<HandledObject> handle) { instancesCount.incrementAndGet(); return new HandledObject(handle); } }; // Borrow 2 * maxCapacity objects. final HandledObject[] array = new HandledObject[maxCapacity * 2]; for (int i = 0; i < array.length; i++) { array[i] = recycler.get(); } assertEquals(array.length, instancesCount.get()); // Reset counter. instancesCount.set(0); // Recycle from other thread. final Thread thread = new Thread() { @Override public void run() { for (HandledObject object: array) { object.recycle(); } } }; thread.start(); thread.join(); assertEquals(0, instancesCount.get()); // Borrow 2 * maxCapacity objects. Half of them should come from // the recycler queue, the other half should be freshly allocated. for (int i = 0; i < array.length; i++) { recycler.get(); } // The implementation uses maxCapacity / 2 as limit per WeakOrderQueue assertTrue("The instances count (" + instancesCount.get() + ") must be <= array.length (" + array.length + ") - maxCapacity (" + maxCapacity + ") / 2 as we not pool all new handles" + " internally", array.length - maxCapacity / 2 <= instancesCount.get()); } static final class HandledObject { Recycler.Handle<HandledObject> handle; HandledObject(Recycler.Handle<HandledObject> handle) { this.handle = handle; } void recycle() { handle.recycle(this); } } }