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 barrier that blocks until all parties have either called or have arrived at the meeting point. 
021 * Very useful for testing or other purposes that require to make concurrent settings deterministic.
022 *
023 * @version $Id: RendezvousBarrier.java 493628 2007-01-07 01:42:48Z joerg $
024 */
025public class RendezvousBarrier {
026
027    public static final int DEFAULT_TIMEOUT = 20000;
028
029    protected final int parties;
030    protected final String name;
031    protected int count = 0;
032    protected long timeout;
033    protected LoggerFacade logger;
034
035    public RendezvousBarrier(String name, LoggerFacade logger) {
036        this(name, DEFAULT_TIMEOUT, logger);
037    }
038
039    public RendezvousBarrier(String name, long timeout, LoggerFacade logger) {
040        this(name, 2, timeout, logger);
041    }
042
043    public RendezvousBarrier(String name, int parties, long timeout, LoggerFacade logger) {
044        this.parties = parties;
045        this.name = name;
046        this.timeout = timeout;
047        this.logger = logger;
048    }
049
050    /**
051     * Notify the barrier that you (the current thread) will not come to the meeting point. 
052     * Same thing as {@link #meet()}, but does not not let you wait.
053     */
054    public synchronized void call() {
055        count++;
056        if (count >= parties) {
057            if (logger.isFineEnabled()) 
058                logger.logFine("Thread " + Thread.currentThread().getName() + " by CALL COMPLETING barrier " + name);
059            notifyAll();
060        }
061    }
062
063    /**
064     * Meet at this barrier. The current thread will either block when there are missing parties for this barrier 
065     * or it is the last one to complete this meeting and the barrier will release its block. 
066     * In this case all other waiting threads will be notified.
067     * 
068     * @throws InterruptedException if the current thread is interrupted while waiting
069     */
070    public synchronized void meet() throws InterruptedException {
071        count++;
072        if (count >= parties) {
073            if (logger.isFineEnabled()) 
074                logger.logFine("Thread " + Thread.currentThread().getName() + " by MEET COMPLETING barrier " + name);
075            notifyAll();
076        } else {
077            if (logger.isFineEnabled()) {
078                    logger.logFine(
079                        "At barrier "
080                            + name
081                            + " thread "
082                            + Thread.currentThread().getName()
083                            + " WAITING for "
084                            + (parties - count)
085                            + " of "
086                            + parties
087                            + " parties");
088            }
089            wait(timeout);
090            if (count == 0) {
091                // means the barrier has been reset
092            } else if (count >= parties) {
093                if (logger.isFineEnabled()) 
094                    logger.logFine("Thread " + Thread.currentThread().getName() + " CONTINUING at barrier " + name);
095            } else {
096                if (logger.isFineEnabled()) 
097                    logger.logFine("Thread " + Thread.currentThread().getName() + " FAILING at barrier " + name);
098                notifyAll();
099            }
100        }
101    }
102
103    /**
104     * Releases all waiting threads and resets the number of parties already arrived. 
105     */
106    public synchronized void reset() {
107        if (logger.isFineEnabled()) logger.logFine("Resetting barrier " + name);
108        count = 0;
109        notifyAll();
110    }
111
112}