/* * 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.codehaus.groovy.util; import org.apache.groovy.stress.util.GCUtils; import org.apache.groovy.stress.util.ThreadUtils; import org.junit.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import static org.junit.Assert.*; public class ManagedConcurrentLinkedQueueStressTest { static final int ENTRY_COUNT = 8196; static final ReferenceBundle bundle = ReferenceBundle.getWeakBundle(); ManagedConcurrentLinkedQueue<Object> queue = new ManagedConcurrentLinkedQueue<Object>(bundle); @Test public void testQueueRemovesCollectedEntries() { // Keep a hardref so we can test get later List<Object> elements = populate(); assertEquals("should contain all entries", ENTRY_COUNT, queue.values().size()); Object o = elements.get(ENTRY_COUNT / 2); assertTrue("should contain an element", queue.values().contains(o)); o = null; elements.remove(0); GCUtils.gc(); assertEquals("should have one less element", ENTRY_COUNT - 1, queue.values().size()); elements.clear(); GCUtils.gc(); // Add an entries to force ReferenceManager.removeStaleEntries Object last = new Object(); queue.add(last); assertEquals("should only contain last added", 1, queue.values().size()); } @Test public void testQueueRemovesCollectedEntriesOnIteration() { List<Object> elements = populate(); assertEquals("should contain all entries", ENTRY_COUNT, queue.values().size()); elements.clear(); GCUtils.gc(); assertFalse("Iterator should remove collected elements", queue.iterator().hasNext()); } @Test public void testQueueIterationManyThreadsWithRemove() throws Exception { List<Object> elements = populate(); assertEquals("should contain all entries", ENTRY_COUNT, queue.values().size()); multipleIterateAndRemove(8, ENTRY_COUNT); } @Test public void testQueueIterationManyThreadsWithRemoveWithGC() throws Exception { List<Object> elements = populate(); assertEquals("should contain all entries", ENTRY_COUNT, queue.values().size()); // Remove some refs so GC will work in order to test multiple iterating threads // removing collected references int i = 0; for (Iterator<Object> itr = elements.iterator(); itr.hasNext();) { itr.next(); if (i++ % 8 == 0) { itr.remove(); } } GCUtils.gc(); multipleIterateAndRemove(8, elements.size()); } @Test public void testQueueRemoveCalledByMultipleThreadsOnSameElement() throws Exception { final Object value1 = new Object(); final Object value2 = new Object(); queue.add(value1); queue.add(value2); final int threadCount = 8; final CyclicBarrier barrier = new CyclicBarrier(threadCount + 1); for (int i = 0; i < threadCount; i++) { Thread t = new Thread(new Runnable() { @Override public void run() { Iterator<Object> itr = queue.iterator(); Object o = itr.next(); assertEquals(value1, o); ThreadUtils.await(barrier); itr.remove(); ThreadUtils.await(barrier); } }); t.setDaemon(true); t.start(); } ThreadUtils.await(barrier); // start barrier.await(1L, TimeUnit.MINUTES); Iterator<Object> itr = queue.iterator(); assertTrue(itr.hasNext()); assertEquals(value2, itr.next()); assertFalse(itr.hasNext()); } private void multipleIterateAndRemove(final int threadCount, final int expectCount) throws Exception { final CyclicBarrier barrier = new CyclicBarrier(threadCount + 1); for (int i = 0; i < threadCount; i++) { final int idx = i; Thread t = new Thread(new Runnable() { @Override public void run() { int elementCount = 0; Iterator<Object> itr = queue.iterator(); ThreadUtils.await(barrier); while (itr.hasNext()) { itr.next(); if (elementCount++ == idx) { itr.remove(); } } assertTrue(elementCount >= (expectCount - threadCount)); ThreadUtils.await(barrier); } }); t.setDaemon(true); t.start(); } ThreadUtils.await(barrier); //start barrier.await(1L, TimeUnit.MINUTES); } private List<Object> populate() { List<Object> elements = new ArrayList<Object>(ENTRY_COUNT); for (int i = 0; i < ENTRY_COUNT; i++) { Object o = new Object(); elements.add(o); queue.add(o); } return elements; } }