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 */
017package 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 */
027public 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}