1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.kernel.concurrent;
16  
17  import java.util.concurrent.locks.AbstractQueuedSynchronizer;
18  
19  /**
20   * <a href="CompeteLatch.java.html"><b><i>View Source</i></b></a>
21   *
22   * <p>
23   * A synchronizer based on the JDK's AQS framework to simulate a single winner
24   * competition. This synchronizer supports cyclical competition. In this
25   * situation, loser threads should try again. The single winner thread will lock
26   * the latch while other threads will block on the latch by calling
27   * <code>await</code>. After the winner thread finishes its job, it should call
28   * <code>done</code> which will open the latch. All blocking loser threads can
29   * pass the latch at the same time.
30   * </p>
31   *
32   * <p>
33   * See LPS-3744 for a sample use case.
34   * </p>
35   *
36   * @author Shuyang Zhou
37   */
38  public class CompeteLatch {
39  
40      /**
41       * This method should only be called by a loser thread. If the latch is
42       * locked, that means the winner is executing its job and all loser threads
43       * that call this method will be blocked. If the latch is not locked, that
44       * means the winner has finished its job and all the loser threads calling
45       * this method will return immediately. If the winner thread calls this
46       * method before his job completed, then all threads will deadlock.
47       */
48      public void await() {
49          _sync.acquireShared(1);
50      }
51  
52      /**
53       * Tells the current thread to join the competition. Return immediately
54       * whether or not the current thread is the winner thread or a loser thread.
55       * No matter how many threads join this competition, only one thread can be
56       * the winner thread.
57       *
58       * @return true if the current thread is the winner thread
59       */
60      public boolean compete() {
61          return _sync._tryInitAcquireShared();
62      }
63  
64      /**
65       * This method should only be called by the winner thread. The winner thread
66       * calls this method to indicate that it has finished its job and unlocks
67       * the latch to allow all loser threads return from the <code>await</code>
68       * method. If a loser thread does call this method when a winner thread has
69       * locked the latch, the latch will break and the winner thread may be put
70       * into a non thread safe state. You should never have to do this except to
71       * get out of a deadlock. If no one threads have locked the latch, then
72       * calling this method has no effect. This method will return immediately.
73       *
74       * @return true if this call opens the latch, false if the latch is already
75       *         open
76       */
77      public boolean done() {
78          return _sync.releaseShared(1);
79      }
80  
81      /**
82       * Returns true if the latch is locked. This method should not be used to
83       * test the latch before joining a competition because it is not thread
84       * safe. The only purpose for this method is to give external systems a way
85       * to monitor the latch which is usually be used for deadlock detection.
86       */
87      public boolean isLocked() {
88          return _sync._isLocked();
89      }
90  
91      private Sync _sync = new Sync();
92  
93      private class Sync extends AbstractQueuedSynchronizer {
94  
95          protected int tryAcquireShared(int arg) {
96              if (getState() == 0) {
97                  return 1;
98              }
99              else {
100                 return -1;
101             }
102         }
103 
104         protected boolean tryReleaseShared(int arg) {
105             if (compareAndSetState(1, 0)) {
106                 return true;
107             }
108             else {
109                 return false;
110             }
111         }
112 
113         private final boolean _isLocked() {
114             if (getState() == 1) {
115                 return true;
116             }
117             else {
118                 return false;
119             }
120         }
121 
122         private final boolean _tryInitAcquireShared() {
123             if (compareAndSetState(0, 1)) {
124                 return true;
125             }
126             else {
127                 return false;
128             }
129         }
130 
131     }
132 
133 }