1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.kernel.concurrent;
24  
25  import java.util.concurrent.locks.AbstractQueuedSynchronizer;
26  
27  /**
28   * <a href="CompeteLatch.java.html"><b><i>View Source</i></b></a>
29   *
30   * <p>
31   * A synchronizer based on the JDK's AQS framework to simulate a single winner
32   * competition. This synchronizer supports cyclical competition. In this
33   * situation, loser threads should try again. The single winner thread will lock
34   * the latch while other threads will block on the latch by calling
35   * <code>await</code>. After the winner thread finishes its job, it should call
36   * <code>done</code> which will open the latch. All blocking loser threads can
37   * pass the latch at the same time.
38   * </p>
39   *
40   * <p>
41   * See LPS-3744 for a sample use case.
42   * </p>
43   *
44   * @author Shuyang Zhou
45   */
46  public class CompeteLatch {
47  
48      /**
49       * This method should only be called by a loser thread. If the latch is
50       * locked, that means the winner is executing its job and all loser threads
51       * that call this method will be blocked. If the latch is not locked, that
52       * means the winner has finished its job and all the loser threads calling
53       * this method will return immediately. If the winner thread calls this
54       * method before his job completed, then all threads will deadlock.
55       */
56      public void await() {
57          _sync.acquireShared(1);
58      }
59  
60      /**
61       * Tells the current thread to join the competition. Return immediately
62       * whether or not the current thread is the winner thread or a loser thread.
63       * No matter how many threads join this competition, only one thread can be
64       * the winner thread.
65       *
66       * @return true if the current thread is the winner thread
67       */
68      public boolean compete() {
69          return _sync._tryInitAcquireShared();
70      }
71  
72      /**
73       * This method should only be called by the winner thread. The winner thread
74       * calls this method to indicate that it has finished its job and unlocks
75       * the latch to allow all loser threads return from the <code>await</code>
76       * method. If a loser thread does call this method when a winner thread has
77       * locked the latch, the latch will break and the winner thread may be put
78       * into a non thread safe state. You should never have to do this except to
79       * get out of a deadlock. If no one threads have locked the latch, then
80       * calling this method has no effect. This method will return immediately.
81       *
82       * @return true if this call opens the latch, false if the latch is already
83       *         open
84       */
85      public boolean done() {
86          return _sync.releaseShared(1);
87      }
88  
89      /**
90       * Returns true if the latch is locked. This method should not be used to
91       * test the latch before joining a competition because it is not thread
92       * safe. The only purpose for this method is to give external systems a way
93       * to monitor the latch which is usually be used for deadlock detection.
94       */
95      public boolean isLocked() {
96          return _sync._isLocked();
97      }
98  
99      private Sync _sync = new Sync();
100 
101     private class Sync extends AbstractQueuedSynchronizer {
102 
103         protected int tryAcquireShared(int arg) {
104             if (getState() == 0) {
105                 return 1;
106             }
107             else {
108                 return -1;
109             }
110         }
111 
112         protected boolean tryReleaseShared(int arg) {
113             if (compareAndSetState(1, 0)) {
114                 return true;
115             }
116             else {
117                 return false;
118             }
119         }
120 
121         private final boolean _isLocked() {
122             if (getState() == 1) {
123                 return true;
124             }
125             else {
126                 return false;
127             }
128         }
129 
130         private final boolean _tryInitAcquireShared() {
131             if (compareAndSetState(0, 1)) {
132                 return true;
133             }
134             else {
135                 return false;
136             }
137         }
138 
139     }
140 
141 }