/* * org.openmicroscopy.shoola.util.concur.TestObjectTransferSync * *------------------------------------------------------------------------------ * Copyright (C) 2006 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.concur; //Java imports //Third-party libraries import junit.framework.TestCase; //Application-internal dependencies /** * Verifies the safety of {@link ObjectTransfer}. * Makes sure that state transitions are correctly serialized. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br>Andrea Falconi      * <a href="mailto:a.falconi@dundee.ac.uk"> * a.falconi@dundee.ac.uk</a> * @version 2.2 * <small> * (<b>Internal version:</b> $Revision$ $Date$) * </small> * @since OME2.2 */ public class TestObjectTransferSync extends TestCase { private ObjectTransfer target; //Object under test. private ThreadSupport collect_1, //Alt flow to do a collect(). collect_2, //Another collect(). handOff_2; //An handOff() - other one in main flow. private class CheckPoint { long transitionTimestamp; //Taken by observer. Object objInTransit; //Grabbed by observer. Object handOffObj; //Passed to handOff. Null for collect threads. Object collectObj; //Returned by collect. Null for hand-off threads. Throwable exc; //To transfer any unexpected error to main flow. } //4 check points: // Empty->Full transition caused by first handOff() in main flow. // Full->Empty transition caused by collect() collect_1 alt flow. // Empty->Full transition caused by handOff() in handOff_2 alt flow. // Full->Empty transition caused by collect() in collect_2 alt flow. private CheckPoint handOff_1_cp, collect_1_cp, handOff_2_cp, collect_2_cp; //Will be handOff_2_cp for handOff_2 alt flow, collect_1_cp for collect_1 //alt flow, and collect_2_cp for collect_2 alt flow. private static ThreadLocal checkPoint; //Called within each thread from observer.update(). private void takeTimestampAndObjInTransit() { CheckPoint cp = (CheckPoint) checkPoint.get(); cp.transitionTimestamp = System.currentTimeMillis(); cp.objInTransit = target.getObjInTransit(); } private class DoHandOff implements Runnable { CheckPoint cp; DoHandOff(CheckPoint cp) { this.cp = cp; } public void run() { try { checkPoint.set(cp); //Now it's visible from within update(). target.handOff(cp.handOffObj); } catch (Throwable interruptedException) { //Should never happen, as we don't interrupt. Moreover, we're //not expecting anything different from IE. But just in case: cp.exc = interruptedException; } } } private class DoCollect implements Runnable { CheckPoint cp; DoCollect(CheckPoint cp) { this.cp = cp; } public void run() { try { checkPoint.set(cp); //Now it's visible from within update(). cp.collectObj = target.collect(); } catch (Throwable interruptedException) { //Should never happen, as we don't interrupt. Moreover, we're //not expecting anything different from IE. But just in case: cp.exc = interruptedException; } } } public void setUp() { //Create new target. target = new ObjectTransfer(); //Create check points. handOff_1_cp = new CheckPoint(); handOff_1_cp.handOffObj = new Object(); collect_1_cp = new CheckPoint(); handOff_2_cp = new CheckPoint(); handOff_2_cp.handOffObj = new Object(); collect_2_cp = new CheckPoint(); //Create the thread local var to hold the check point for each thread. checkPoint = new ThreadLocal(); //Link handOff_1_cp to main flow. checkPoint.set(handOff_1_cp); //Create alternate flows and links them to their check point. collect_1 = new ThreadSupport(new DoCollect(collect_1_cp)); collect_2 = new ThreadSupport(new DoCollect(collect_2_cp)); handOff_2 = new ThreadSupport(new DoHandOff(handOff_2_cp)); //Create observer. target.register(new ControlFlowObserver() { public void update(int checkPointID) { if (collect_1.isMainFlow()) { //Start alternate flows. collect_1.startAltFlow(); collect_2.startAltFlow(); handOff_2.startAltFlow(); //Pause to make (almost) sure we get concurrent access. collect_1.pauseMainFlow(); } takeTimestampAndObjInTransit(); } }); } public void test() throws InterruptedException { //Do the first hand-off. This will spawn the alternate threads. target.handOff(handOff_1_cp.handOffObj); //Wait for all threads to die and their working memory to be flushed. collect_1.awaitAltFlow(); collect_2.awaitAltFlow(); handOff_2.awaitAltFlow(); //Now order the check point objects. If transitions were serialized //correctly, then the order is either // handOff_1_cp -> collect_1_cp -> handOff_2_cp -> collect_2_cp //or // handOff_1_cp -> collect_2_cp -> handOff_2_cp -> collect_1_cp //(We don't know which collect happened first.) CheckPoint[] cps = new CheckPoint[] {handOff_1_cp, collect_1_cp, handOff_2_cp, collect_2_cp}; if (collect_2_cp.transitionTimestamp < collect_1_cp.transitionTimestamp) { cps[1] = collect_2_cp; cps[3] = collect_1_cp; } //Test. /* assertTrue("First Empty->Full transition wasn't correctly serialized.", cps[0].transitionTimestamp <= cps[1].transitionTimestamp); assertNull("First Empty->Full transition wasn't correctly serialized.", cps[0].objInTransit); assertTrue("First Full->Empty transition wasn't correctly serialized.", cps[1].transitionTimestamp <= cps[2].transitionTimestamp); assertNotNull("First Full->Empty transition wasn't correctly " + "serialized.", cps[1].objInTransit); assertSame("First Full->Empty transition wasn't correctly serialized.", cps[0].handOffObj, cps[1].collectObj); assertTrue("Second Empty->Full transition wasn't correctly serialized.", cps[2].transitionTimestamp <= cps[3].transitionTimestamp); assertNull("Second Empty->Full transition wasn't correctly serialized.", cps[2].objInTransit); assertNotNull("Second Full->Empty transition wasn't correctly " + "serialized.", cps[3].objInTransit); assertSame("Second Full->Empty transition wasn't correctly serialized.", cps[2].handOffObj, cps[3].collectObj); */ } /* NOTE: correct serialization => what asserted above * However, <= is not, in general, true -- it could happen b/c of a lucky * combination of events, but timeouts we chose make that combination quite * unlikely. */ }