001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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     * A synchronizer based on the JDK's AQS framework to simulate a single winner
022     * competition. This synchronizer supports cyclical competition. In this
023     * situation, loser threads should try again. The single winner thread will lock
024     * the latch while other threads will block on the latch by calling
025     * <code>await</code>. After the winner thread finishes its job, it should call
026     * <code>done</code> which will open the latch. All blocking loser threads can
027     * pass the latch at the same time.
028     *
029     * <p>
030     * See LPS-3744 for a sample use case.
031     * </p>
032     *
033     * @author Shuyang Zhou
034     */
035    public class CompeteLatch {
036    
037            /**
038             * This method should only be called by a loser thread. If the latch is
039             * locked, that means the winner is executing its job and all loser threads
040             * that call this method will be blocked. If the latch is not locked, that
041             * means the winner has finished its job and all the loser threads calling
042             * this method will return immediately. If the winner thread calls this
043             * method before his job completed, then all threads will deadlock.
044             *
045             * @throws InterruptedException if the current thread is interrupted
046             */
047            public void await() throws InterruptedException {
048                    _sync.acquireSharedInterruptibly(1);
049            }
050    
051            /**
052             * This method should only be called by a loser thread. If the latch is
053             * locked, that means the winner is executing its job and all loser threads
054             * that call this method will be blocked for the given waiting time. If the
055             * latch is not locked, that means the winner has finished its job and all
056             * the loser threads calling this method will return immediately. If the
057             * winner thread calls this method before his job completed, then all
058             * threads will deadlock.
059             *
060             * @param  timeout the timeout value
061             * @param  timeUnit the time unit
062             * @return <code>true</code> if the latch was open, <code>false</code> if
063             *         the waiting time elapsed 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 <code>true</code> if this call opens the latch,
095             *         <code>false</code> if the 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             * @return <code>true</code> if the latch is locked; <code>false</code>
109             *         otherwise
110             */
111            public boolean isLocked() {
112                    return _sync._isLocked();
113            }
114    
115            private Sync _sync = new Sync();
116    
117            private class Sync extends AbstractQueuedSynchronizer {
118    
119                    @Override
120                    protected int tryAcquireShared(int arg) {
121                            if (getState() == 0) {
122                                    return 1;
123                            }
124                            else {
125                                    return -1;
126                            }
127                    }
128    
129                    @Override
130                    protected boolean tryReleaseShared(int arg) {
131                            if (compareAndSetState(1, 0)) {
132                                    return true;
133                            }
134                            else {
135                                    return false;
136                            }
137                    }
138    
139                    private boolean _isLocked() {
140                            if (getState() == 1) {
141                                    return true;
142                            }
143                            else {
144                                    return false;
145                            }
146                    }
147    
148                    private boolean _tryInitAcquireShared() {
149                            if (compareAndSetState(0, 1)) {
150                                    return true;
151                            }
152                            else {
153                                    return false;
154                            }
155                    }
156    
157            }
158    
159    }