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