001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.transaction.util;
018    
019    /**
020     * Simple turn based barrier to make a sequence of calls from different threads deterministic.
021     * This is very useful for testing where you want to have a continuous flow throughout 
022     * different threads. The idea is to have an ordered sequence of steps where step n can not be 
023     * executed before n-1.
024     * 
025     * @version $Id: TurnBarrier.java 493628 2007-01-07 01:42:48Z joerg $
026     */
027    public class TurnBarrier {
028    
029        public static final long DEFAULT_TIMEOUT = Long.MAX_VALUE;
030    
031        protected final String name;
032    
033        protected int currentNumber;
034    
035        protected final int startNumber;
036    
037        protected final long timeout;
038    
039        protected LoggerFacade logger;
040    
041        /**
042         * Creates a new turn barrier starting with turn 0 with an unlimited timeout.
043         * 
044         * @param name the name of the barrier
045         * @param logger logger for debug output
046         */
047        public TurnBarrier(String name, LoggerFacade logger) {
048            this(name, DEFAULT_TIMEOUT, logger);
049        }
050    
051        /**
052         * Creates a new turn barrier starting with turn 0.
053         * 
054         * @param name the name of the barrier
055         * @param timeout timeout for threads to wait for their turn
056         * @param logger logger for debug output
057         */
058        public TurnBarrier(String name, long timeout, LoggerFacade logger) {
059            this(name, timeout, logger, 0);
060        }
061    
062        /**
063         * Creates a new turn barrier.
064         * 
065         * @param name the name of the barrier
066         * @param timeout timeout for threads to wait for their turn
067         * @param logger logger for debug output
068         * @param startTurn the turn to start with
069         */
070        public TurnBarrier(String name, long timeout, LoggerFacade logger, int startTurn) {
071            this.name = name;
072            this.timeout = timeout;
073            this.logger = logger;
074            this.startNumber = startTurn;
075            this.currentNumber = startTurn;
076        }
077    
078        /**
079         * Blockingly waits for the given turn. If a timeout occurs a runtime exception will be thrown.
080         * 
081         * @param turnNumber the turn number to wait for
082         * @throws InterruptedException thrown if the thread is interrupted while waiting
083         * @throws RuntimeException thrown when timed out
084         */
085        public synchronized void waitForTurn(int turnNumber) throws InterruptedException,
086                RuntimeException {
087            if (turnNumber > currentNumber) {
088                long started = System.currentTimeMillis();
089                for (long remaining = timeout; remaining > 0 && turnNumber > currentNumber; remaining = timeout
090                        - (System.currentTimeMillis() - started)) {
091                    wait(remaining);
092                }
093            }
094            if (turnNumber > currentNumber) {
095                throw new RuntimeException("Timed out while waiting for our turn");
096            }
097        }
098    
099        /**
100         * Signals the next turn. Any thread waiting for the turn will be allowed to continue.
101         * 
102         * @param turnNumber the next turn number
103         */
104        public synchronized void signalTurn(int turnNumber) {
105            currentNumber = turnNumber;
106            notifyAll();
107        }
108        
109        /**
110         * Starts the barrier over again. The next turn will be the one the barrier was started with.
111         *
112         */
113        public synchronized void reset() {
114            signalTurn(startNumber);
115        }
116    }