001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.kernel.concurrent;
016    
017    import java.util.concurrent.TimeUnit;
018    import java.util.concurrent.locks.AbstractQueuedSynchronizer;
019    
020    /**
021     * <p>
022     * A synchronizer based on the JDK's AQS framework to simulate a single winner
023     * competition. This synchronizer supports cyclical competition. In this
024     * situation, loser threads should try again. The single winner thread will lock
025     * the latch while other threads will block on the latch by calling
026     * <code>await</code>. After the winner thread finishes its job, it should call
027     * <code>done</code> which will open the latch. All blocking loser threads can
028     * pass the latch at the same time.
029     * </p>
030     *
031     * <p>
032     * See LPS-3744 for a sample use case.
033     * </p>
034     *
035     * @author Shuyang Zhou
036     */
037    public class CompeteLatch {
038    
039            /**
040             * This method should only be called by a loser thread. If the latch is
041             * locked, that means the winner is executing its job and all loser threads
042             * that call this method will be blocked. If the latch is not locked, that
043             * means the winner has finished its job and all the loser threads calling
044             * this method will return immediately. If the winner thread calls this
045             * method before his job completed, then all threads will deadlock.
046             *
047             * @throws InterruptedException if the current thread is interrupted
048             */
049            public void await() throws InterruptedException {
050                    _sync.acquireSharedInterruptibly(1);
051            }
052    
053            /**
054             * This method should only be called by a loser thread. If the latch is
055             * locked, that means the winner is executing its job and all loser threads
056             * that call this method will be blocked for the given waiting time. If the
057             * latch is not locked, that means the winner has finished its job and all
058             * the loser threads calling this method will return immediately. If the
059             * winner thread calls this method before his job completed, then all
060             * threads will deadlock.
061             *
062             * @return true if the latch was open, false if the waiting time elapsed
063             *                 before the latch be opened.
064             * @throws InterruptedException if the current thread is interrupted
065             */
066            public boolean await(long timeout, TimeUnit timeUnit)
067                    throws InterruptedException {
068    
069                    return _sync.tryAcquireSharedNanos(1, timeUnit.toNanos(timeout));
070            }
071    
072            /**
073             * Tells the current thread to join the competition. Return immediately
074             * whether or not the current thread is the winner thread or a loser thread.
075             * No matter how many threads join this competition, only one thread can be
076             * the winner thread.
077             *
078             * @return <code>true</code> if the current thread is the winner thread
079             */
080            public boolean compete() {
081                    return _sync._tryInitAcquireShared();
082            }
083    
084            /**
085             * This method should only be called by the winner thread. The winner thread
086             * calls this method to indicate that it has finished its job and unlocks
087             * the latch to allow all loser threads return from the <code>await</code>
088             * method. If a loser thread does call this method when a winner thread has
089             * locked the latch, the latch will break and the winner thread may be put
090             * into a non thread safe state. You should never have to do this except to
091             * get out of a deadlock. If no one threads have locked the latch, then
092             * calling this method has no effect. This method will return immediately.
093             *
094             * @return true if this call opens the latch, <code>false</code> if the
095             *                 latch is already open
096             */
097            public boolean done() {
098                    return _sync.releaseShared(1);
099            }
100    
101            /**
102             * Returns <code>true</code> if the latch is locked. This method should not
103             * be used to test the latch before joining a competition because it is not
104             * thread safe. The only purpose for this method is to give external systems
105             * a way to monitor the latch which is usually be used for deadlock
106             * detection.
107             */
108            public boolean isLocked() {
109                    return _sync._isLocked();
110            }
111    
112            private Sync _sync = new Sync();
113    
114            private class Sync extends AbstractQueuedSynchronizer {
115    
116                    protected int tryAcquireShared(int arg) {
117                            if (getState() == 0) {
118                                    return 1;
119                            }
120                            else {
121                                    return -1;
122                            }
123                    }
124    
125                    protected boolean tryReleaseShared(int arg) {
126                            if (compareAndSetState(1, 0)) {
127                                    return true;
128                            }
129                            else {
130                                    return false;
131                            }
132                    }
133    
134                    private boolean _isLocked() {
135                            if (getState() == 1) {
136                                    return true;
137                            }
138                            else {
139                                    return false;
140                            }
141                    }
142    
143                    private boolean _tryInitAcquireShared() {
144                            if (compareAndSetState(0, 1)) {
145                                    return true;
146                            }
147                            else {
148                                    return false;
149                            }
150                    }
151    
152            }
153    
154    }