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 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     */
025    public 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    }