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,
071: int startTurn) {
072: this .name = name;
073: this .timeout = timeout;
074: this .logger = logger;
075: this .startNumber = startTurn;
076: this .currentNumber = startTurn;
077: }
078:
079: /**
080: * Blockingly waits for the given turn. If a timeout occurs a runtime exception will be thrown.
081: *
082: * @param turnNumber the turn number to wait for
083: * @throws InterruptedException thrown if the thread is interrupted while waiting
084: * @throws RuntimeException thrown when timed out
085: */
086: public synchronized void waitForTurn(int turnNumber)
087: throws InterruptedException, RuntimeException {
088: if (turnNumber > currentNumber) {
089: long started = System.currentTimeMillis();
090: for (long remaining = timeout; remaining > 0
091: && turnNumber > currentNumber; remaining = timeout
092: - (System.currentTimeMillis() - started)) {
093: wait(remaining);
094: }
095: }
096: if (turnNumber > currentNumber) {
097: throw new RuntimeException(
098: "Timed out while waiting for our turn");
099: }
100: }
101:
102: /**
103: * Signals the next turn. Any thread waiting for the turn will be allowed to continue.
104: *
105: * @param turnNumber the next turn number
106: */
107: public synchronized void signalTurn(int turnNumber) {
108: currentNumber = turnNumber;
109: notifyAll();
110: }
111:
112: /**
113: * Starts the barrier over again. The next turn will be the one the barrier was started with.
114: *
115: */
116: public synchronized void reset() {
117: signalTurn(startNumber);
118: }
119: }
|