/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.utility.deque; import org.junit.BeforeClass; import org.junit.Test; import org.junit.Assert; import org.mmtk.harness.Harness; import org.mmtk.harness.lang.Env; import org.mmtk.harness.scheduler.Schedulable; import org.mmtk.harness.scheduler.Scheduler; import org.mmtk.plan.Plan; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.ObjectReference; /** * Junit unit-tests for ObjectReferenceDeque. */ public class ObjectReferenceDequeTest { @BeforeClass public static void setUpBeforeClass() throws Exception { /* * Setting options here can be handy for running in Eclipse. Otherwise * allow the harness to pick up the defaults so we can set them using * properties from ant. */ Harness.init("collectors=0" // ,"scheduler=JAVA" // ,"scheduler=DETERMINISTIC" // ,"schedulerPolicy=FIXED" // ,"yieldInterval=1" // ,"schedulerPolicy=RANDOM" // ,"randomPolicyLength=20" // ,"randomPolicyMin=1" // ,"randomPolicyMax=2" // ,"randomPolicySeed=1" // ,"policyStats=true" ); /* The deques rely on being run during GC. */ Plan.setGCStatus(Plan.GC_PROPER); } /** * Helper method to create object references * @param val * @return */ private static ObjectReference o(int val) { return Address.fromIntSignExtend(val).toObjectReference(); } /** * Run a test, ie a list of threads, all run in GC context. * @param items */ private void runTest(final Schedulable... items) { for (Schedulable item : items) { Scheduler.scheduleCollector(item); } Scheduler.scheduleGcThreads(); } @Test public void testPushPop() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque = new ObjectReferenceDeque("deque",shared); shared.prepareNonBlocking(); deque.push(o(1)); Assert.assertEquals(deque.pop(),o(1)); Assert.assertTrue(deque.isEmpty()); } }); } @Test public void testInsertPop() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque = new ObjectReferenceDeque("deque",shared); shared.prepareNonBlocking(); deque.insert(o(1)); Assert.assertEquals(deque.pop(),o(1)); Assert.assertTrue(deque.isEmpty()); } }); } @Test public void testPushPop2() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque = new ObjectReferenceDeque("deque",shared); shared.prepareNonBlocking(); deque.push(o(1)); deque.push(o(2)); Assert.assertEquals(deque.pop(),o(2)); Assert.assertEquals(deque.pop(),o(1)); Assert.assertTrue(deque.isEmpty()); } }); } @Test public void testInsertPop2() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque = new ObjectReferenceDeque("deque",shared); shared.prepareNonBlocking(); deque.insert(o(1)); deque.insert(o(2)); Assert.assertEquals(deque.pop(),o(1)); Assert.assertEquals(deque.pop(),o(2)); Assert.assertTrue(deque.isEmpty()); } }); } @Test public void testPushFlushPop() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque = new ObjectReferenceDeque("deque",shared); shared.prepareNonBlocking(); deque.push(o(1)); deque.flushLocal(); Assert.assertEquals(deque.pop(),o(1)); Assert.assertTrue(deque.isEmpty()); } }); } @Test public void testPushFlushPop2() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque = new ObjectReferenceDeque("deque",shared); shared.prepareNonBlocking(); deque.push(o(1)); deque.push(o(2)); deque.flushLocal(); Assert.assertEquals(deque.pop(),o(2)); Assert.assertEquals(deque.pop(),o(1)); Assert.assertTrue(deque.isEmpty()); } }); } @Test public void test2Heads() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque1 = new ObjectReferenceDeque("deque1",shared); ObjectReferenceDeque deque2 = new ObjectReferenceDeque("deque2",shared); shared.prepareNonBlocking(); deque1.push(o(101)); deque1.push(o(102)); deque2.push(o(201)); deque2.push(o(202)); deque1.flushLocal(); deque2.flushLocal(); Assert.assertEquals(deque1.pop(),o(202)); Assert.assertEquals(deque1.pop(),o(201)); Assert.assertEquals(deque1.pop(),o(102)); Assert.assertEquals(deque1.pop(),o(101)); Assert.assertTrue(deque1.isEmpty()); Assert.assertTrue(deque2.isEmpty()); } }); } @Test public void testnHeads() { final int NDEQUES = 4; final int ENTRIES = 1500; // Page and a half runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque[] deques = new ObjectReferenceDeque[NDEQUES]; shared.prepareNonBlocking(); for (int d=0; d < NDEQUES; d++) { deques[d] = new ObjectReferenceDeque("deque+d",shared); for (int i=0; i < ENTRIES; i++) { deques[d].push(o(d*100000+i+1)); } deques[d].flushLocal(); } for (int i=0; i < ENTRIES * NDEQUES; i++) { Assert.assertFalse(deques[0].pop().isNull()); } for (int d=0; d < NDEQUES; d++) { Assert.assertTrue(deques[d].isEmpty()); } shared.reset(); } }); } @Test public void testPopFlush() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque = new ObjectReferenceDeque("deque",shared); shared.prepareNonBlocking(); for (int i=1; i < 10000; i++) { deque.push(o(i)); } for (int i=1; i < 10000; i++) { deque.push(o(i)); Assert.assertFalse(deque.pop().isNull()); Assert.assertFalse(deque.pop().isNull()); deque.flushLocal(); } Assert.assertTrue(deque.isEmpty()); shared.reset(); } }); } @Test public void testPopFlush2() { runTest(new Schedulable() { @Override public void execute(Env env) { SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); ObjectReferenceDeque deque1 = new ObjectReferenceDeque("deque1",shared); ObjectReferenceDeque deque2= new ObjectReferenceDeque("deque2",shared); shared.prepareNonBlocking(); for (int i=1; i < 10000; i++) { deque1.push(o(i)); deque2.push(o(i)); } for (int i=1; i < 10000; i++) { deque1.push(o(i)); Assert.assertFalse(deque1.pop().isNull()); Assert.assertFalse(deque1.pop().isNull()); deque1.flushLocal(); deque2.push(o(i)); Assert.assertFalse(deque2.pop().isNull()); Assert.assertFalse(deque2.pop().isNull()); deque2.flushLocal(); } Assert.assertTrue(deque1.isEmpty()); Assert.assertTrue(deque2.isEmpty()); shared.reset(); } }); } /**********************************************************************/ int counter = 0; /** * A Collector-context thread that adds 'ins' ObjectReferences * into a shared Deque. A family of 'n' InsertThreads with ordinal * 0,1,...,n-1 will insert all the numbers 0..(n*ins)-1 into * the deque, then pop entries until the Deque is entry. */ abstract class AddRemoveThread implements Schedulable { private final int n; private final int ordinal; private final SharedDeque shared; private final int ins; protected final ObjectReferenceDeque deque; public AddRemoveThread(SharedDeque shared, int n, int ordinal, int ins) { this.shared = shared; this.n = n; this.ordinal = ordinal; this.ins = ins; this.deque = new ObjectReferenceDeque("deque"+ordinal,shared); } @Override public void execute(Env env) { for (int i=0; i < ins; i++) { add(o(1+i*n+ordinal)); } deque.flushLocal(); synchronized(shared) { counter += ins; } int d=0; while (!deque.isEmpty()) { d++; Assert.assertFalse(deque.pop().isNull()); synchronized(shared) { counter--; } } //System.out.printf("Thread %d pushed %d items, popped %d%n",ordinal,ins,d); } protected abstract void add(ObjectReference o); } /** * An AddRemoveThread that 'push'es into the deque */ private class PushThread extends AddRemoveThread { public PushThread(SharedDeque shared, int n, int ordinal, int ins) { super(shared, n, ordinal, ins); } @Override protected void add(ObjectReference o) { deque.push(o); } } /** * An AddRemoveThread that 'insert's into the deque */ private class InsertThread extends AddRemoveThread { public InsertThread(SharedDeque shared, int n, int ordinal, int ins) { super(shared, n, ordinal, ins); } @Override protected void add(ObjectReference o) { deque.insert(o); } } @Test public void testConcurrentPush() { final SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); shared.prepareNonBlocking(); final int N = 4000; counter = 0; runTest( new PushThread(shared,8,0,N), new PushThread(shared,8,1,N), new PushThread(shared,8,2,N), new PushThread(shared,8,3,N), new PushThread(shared,8,4,N), new PushThread(shared,8,5,N), new PushThread(shared,8,6,N), new PushThread(shared,8,7,N)); Assert.assertTrue(counter == 0); shared.reset(); } @Test public void testConcurrentInsert() { final SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); shared.prepareNonBlocking(); final int N = 4000; counter = 0; runTest( new InsertThread(shared,8,0,N), new InsertThread(shared,8,1,N), new InsertThread(shared,8,2,N), new InsertThread(shared,8,3,N), new InsertThread(shared,8,4,N), new InsertThread(shared,8,5,N), new InsertThread(shared,8,6,N), new InsertThread(shared,8,7,N)); Assert.assertTrue(counter == 0); shared.reset(); } @Test public void testConcurrentPushInsert() { final SharedDeque shared = new SharedDeque("shared",Plan.metaDataSpace,1); shared.prepareNonBlocking(); final int N = 4000; counter = 0; runTest( new InsertThread(shared,8,0,N), new PushThread(shared,8,1,N), new InsertThread(shared,8,2,N), new PushThread(shared,8,3,N), new InsertThread(shared,8,4,N), new PushThread(shared,8,5,N), new InsertThread(shared,8,6,N), new PushThread(shared,8,7,N)); Assert.assertTrue(counter == 0); shared.reset(); } }