/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.scenegraph.tests; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertSame; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.ParentNode; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.G2DSceneGraph; import org.simantics.scenegraph.g2d.nodes.DataNode; import org.simantics.scenegraph.g2d.nodes.NavigationNode; import org.simantics.scenegraph.utils.NodeUtil; /** * @author Tuukka Lehtonen */ public class LookupServiceSynchronizationTest { public static final int ITERATIONS = 10; public static final int ITERATION_TIME_MS = 1000; public static final int THREADS = 16; AtomicBoolean endTest; CyclicBarrier startBarrier; Semaphore ended; G2DSceneGraph root = new G2DSceneGraph(); List nodes = new ArrayList(); List nodeIds = new ArrayList(); List mappedIds = new ArrayList(); List errors = new ArrayList(); public class Lookup implements Runnable { String id; INode node; public Lookup(String id, INode node) { this.id = id; this.node = node; } @Override public void run() { try { System.out.println("started " + Thread.currentThread().getName()); System.out.flush(); startBarrier.await(); while (!endTest.get()) { Thread.yield(); INode n = NodeUtil.lookup(this.node, this.id); String id = NodeUtil.lookupId(this.node); if (n != null) assertSame(this.node, n); if (id != null) assertSame(this.id, id); } } catch (InterruptedException e) { errors.add(e); } catch (BrokenBarrierException e) { errors.add(e); } finally { System.out.println("ending " + Thread.currentThread().getName()); System.out.flush(); ended.release(); } } } public class Mapper implements Runnable { String id; INode node; public Mapper(String id, INode node) { this.id = id; this.node = node; } @Override public void run() { try { System.out.println("started " + Thread.currentThread().getName()); System.out.flush(); startBarrier.await(); for (int i = 0; !endTest.get(); ++i) { Thread.yield(); NodeUtil.map(node, id); if ((i & 1) == 0) NodeUtil.unmap(node); else root.unmap(id); } } catch (InterruptedException e) { errors.add(e); } catch (BrokenBarrierException e) { errors.add(e); } finally { System.out.println("ending " + Thread.currentThread().getName()); System.out.flush(); ended.release(); } } } T addAndMapNode(ParentNode parent, String id, Class clazz) { T node = parent.addNode(id, clazz); nodes.add( node ); nodeIds.add( id ); NodeUtil.map(node, id); return node; } T addNode(ParentNode parent, String id, Class clazz) { T node = parent.addNode(id, clazz); nodes.add( node ); nodeIds.add( id ); return node; } T addNode(ParentNode parent, Class clazz) { return addNode(parent, UUID.randomUUID().toString(), clazz); } @Test public void testLookup() throws Exception { NavigationNode nav = addAndMapNode(root, "navigation", NavigationNode.class); DataNode data = addAndMapNode(root, "data", DataNode.class); G2DParentNode elements = addNode(nav, "elements", G2DParentNode.class); @SuppressWarnings("unused") G2DParentNode ghosts = addNode(nav, "ghosts", G2DParentNode.class); // Basic checks assertSame(nav, root.lookupNode("navigation")); assertSame(data, root.lookupNode("data")); assertSame(nav, NodeUtil.lookup(nav, "navigation")); assertSame(data, NodeUtil.lookup(data, "data")); for (int iter = 1; iter <= ITERATIONS; ++iter) { System.out.println("Starting iteration " + iter); System.out.flush(); // Start iteration endTest = new AtomicBoolean(false); startBarrier = new CyclicBarrier(THREADS * 2 + 1); ended = new Semaphore(0); for (int i = 0; i < THREADS; ++i) { String id = UUID.randomUUID().toString(); INode node = addNode(elements, id, G2DParentNode.class); new Thread(new Mapper(id, node), "Mapper-" + i).start(); new Thread(new Lookup(id, node), "Lookup-" + i).start(); } startBarrier.await(); // Sleep for the testing period synchronized (this) { wait(ITERATION_TIME_MS); } // Wait for threads to end System.out.println("Ending iteration " + iter); System.out.flush(); endTest.set(true); ended.acquire(2*THREADS); System.out.println("Iteration " + iter + " ended"); } assertEquals(errors.size(), 0); } }