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 }