1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions 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
34    * lock 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    */
47  public class CompeteLatch {
48  
49      /**
50       * This method should only be called by a loser thread. If the latch is
51       * locked, that means the winner is executing its job and all loser threads
52       * that call this method will be blocked. If the latch is not locked, that
53       * means the winner has finished its job and all the loser threads calling
54       * this method will return immediately. If the winner thread calls this
55       * method before his job completed, then all threads will deadlock.
56       */
57      public void await() {
58          _sync.acquireShared(1);
59      }
60  
61      /**
62       * Tells the current thread to join the competition. Return immediately
63       * whether or not the current thread is the winner thread or a loser thread.
64       * No matter how many threads join this competition, only one thread can be
65       * the winner thread.
66       *
67       * @return      true if the current thread is the winner thread
68       */
69      public boolean compete() {
70          return _sync._tryInitAcquireShared();
71      }
72  
73      /**
74       * This method should only be called by the winner thread. The winner thread
75       * calls this method to indicate that it has finished its job and unlocks
76       * the latch to allow all loser threads return from the <code>await</code>
77       * method. If a loser thread does call this method when a winner thread has
78       * locked the latch, the latch will break and the winner thread may be put
79       * into a non thread safe state. You should never have to do this except to
80       * get out of a deadlock. If no one threads have locked the latch, then
81       * calling this method has no effect. This method will return immediately.
82       *
83       * @return      true if this call opens the latch, false if the latch is
84       *              already open
85       */
86      public boolean done() {
87          return _sync.releaseShared(1);
88      }
89  
90      /**
91       * Returns true if the latch is locked. This method should not be used to
92       * test the latch before joining a competition because it is not thread
93       * safe. The only purpose for this method is to give external systems a way
94       * to monitor the latch which is usually be used for deadlock detection.
95       */
96      public boolean isLocked(){
97          return _sync._isLocked();
98      }
99  
100     private Sync _sync = new Sync();
101 
102     private class Sync extends AbstractQueuedSynchronizer {
103 
104         protected int tryAcquireShared(int arg) {
105             if (getState() == 0) {
106                 return 1;
107             }
108             else {
109                 return -1;
110             }
111         }
112 
113         protected boolean tryReleaseShared(int arg) {
114             if (compareAndSetState(1, 0)) {
115                 return true;
116             }
117             else {
118                 return false;
119             }
120         }
121 
122         private final boolean _isLocked(){
123             if (getState() == 1) {
124                 return true;
125             }
126             else {
127                 return false;
128             }
129         }
130 
131         private final boolean _tryInitAcquireShared() {
132             if (compareAndSetState(0, 1)) {
133                 return true;
134             }
135             else {
136                 return false;
137             }
138         }
139 
140     }
141 
142 }