/* * JBoss, Home of Professional Open Source * Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.messaging.tests.unit.util; import java.util.concurrent.CountDownLatch; import junit.framework.TestCase; import org.jboss.messaging.core.logging.Logger; import org.jboss.messaging.util.VariableLatch; /** * * @author <a href="mailto:clebert.suconic@jboss.com">Clebert Suconic</a> * */ public class VariableLatchTest extends TestCase { private static final Logger log = Logger.getLogger(VariableLatchTest.class); public void testLatchOnSingleThread() throws Exception { VariableLatch latch = new VariableLatch(); for (int i = 1; i <= 100; i++) { latch.up(); assertEquals(i, latch.getCount()); } for (int i = 100; i > 0; i--) { assertEquals(i, latch.getCount()); latch.down(); assertEquals(i - 1, latch.getCount()); } latch.waitCompletion(); } /** * * This test will open numberOfThreads threads, and add numberOfAdds on the * VariableLatch After those addthreads are finished, the latch count should * be numberOfThreads * numberOfAdds Then it will open numberOfThreads * threads again releasing numberOfAdds on the VariableLatch After those * releaseThreads are finished, the latch count should be 0 And all the * waiting threads should be finished also * * @throws Exception */ public void testLatchOnMultiThread() throws Exception { final VariableLatch latch = new VariableLatch(); latch.up(); // We hold at least one, so ThreadWaits won't go away final int numberOfThreads = 100; final int numberOfAdds = 100; class ThreadWait extends Thread { private volatile boolean waiting = true; public void run() { try { if (!latch.waitCompletion(5000)) { log.error("Latch timed out"); } } catch (Exception e) { log.error(e); } waiting = false; } } class ThreadAdd extends Thread { private final CountDownLatch latchReady; private final CountDownLatch latchStart; ThreadAdd(final CountDownLatch latchReady, final CountDownLatch latchStart) { this.latchReady = latchReady; this.latchStart = latchStart; } public void run() { try { latchReady.countDown(); // Everybody should start at the same time, to worse concurrency // effects latchStart.await(); for (int i = 0; i < numberOfAdds; i++) { latch.up(); } } catch (Exception e) { log.error(e.getMessage(), e); } } } CountDownLatch latchReady = new CountDownLatch(numberOfThreads); CountDownLatch latchStart = new CountDownLatch(1); ThreadAdd[] threadAdds = new ThreadAdd[numberOfThreads]; ThreadWait waits[] = new ThreadWait[numberOfThreads]; for (int i = 0; i < numberOfThreads; i++) { threadAdds[i] = new ThreadAdd(latchReady, latchStart); threadAdds[i].start(); waits[i] = new ThreadWait(); waits[i].start(); } latchReady.await(); latchStart.countDown(); for (int i = 0; i < numberOfThreads; i++) { threadAdds[i].join(); } for (int i = 0; i < numberOfThreads; i++) { assertTrue(waits[i].waiting); } assertEquals(numberOfThreads * numberOfAdds + 1, latch.getCount()); class ThreadDown extends Thread { private final CountDownLatch latchReady; private final CountDownLatch latchStart; ThreadDown(final CountDownLatch latchReady, final CountDownLatch latchStart) { this.latchReady = latchReady; this.latchStart = latchStart; } public void run() { try { latchReady.countDown(); // Everybody should start at the same time, to worse concurrency // effects latchStart.await(); for (int i = 0; i < numberOfAdds; i++) { latch.down(); } } catch (Exception e) { log.error(e.getMessage(), e); } } } latchReady = new CountDownLatch(numberOfThreads); latchStart = new CountDownLatch(1); ThreadDown down[] = new ThreadDown[numberOfThreads]; for (int i = 0; i < numberOfThreads; i++) { down[i] = new ThreadDown(latchReady, latchStart); down[i].start(); } latchReady.await(); latchStart.countDown(); for (int i = 0; i < numberOfThreads; i++) { down[i].join(); } assertEquals(1, latch.getCount()); for (int i = 0; i < numberOfThreads; i++) { assertTrue(waits[i].waiting); } latch.down(); for (int i = 0; i < numberOfThreads; i++) { waits[i].join(); } assertEquals(0, latch.getCount()); for (int i = 0; i < numberOfThreads; i++) { assertFalse(waits[i].waiting); } } public void testReuseLatch() throws Exception { final VariableLatch latch = new VariableLatch(); latch.up(); class ThreadWait extends Thread { private volatile boolean waiting = false; private volatile Exception e; private final CountDownLatch readyLatch = new CountDownLatch(1); public void run() { waiting = true; readyLatch.countDown(); try { if (!latch.waitCompletion(1000)) { log.error("Latch timed out!", new Exception ("trace")); } } catch (Exception e) { log.error(e); this.e = e; } waiting = false; } } ThreadWait t = new ThreadWait(); t.start(); t.readyLatch.await(); assertEquals(true, t.waiting); latch.down(); t.join(); assertEquals(false, t.waiting); assertNull(t.e); latch.up(); t = new ThreadWait(); t.start(); t.readyLatch.await(); assertEquals(true, t.waiting); latch.down(); t.join(); assertEquals(false, t.waiting); assertNull(t.e); assertTrue(latch.waitCompletion(1000)); assertEquals(0, latch.getCount()); latch.down(); assertEquals(0, latch.getCount()); } }