View Javadoc

1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package com.atlassian.util.concurrent;
18  
19  import net.jcip.annotations.ThreadSafe;
20  
21  import java.util.concurrent.TimeUnit;
22  import java.util.concurrent.locks.AbstractQueuedSynchronizer;
23  import java.util.concurrent.locks.Condition;
24  
25  /**
26   * A {@link BooleanLatch} is a reusable latch that resets after it is released
27   * and waited on. It depends on a boolean condition of being released or not and
28   * becomes unreleased when one thread successfully awaits it. It is useful for
29   * rally like release-wait-release coordination, and as a replacement to waiting
30   * on a {@link Condition} (it should be faster as the write thread does not need
31   * to acquire a lock in order to signal.
32   * <p>
33   * This latch is suitable for SRSW coordination. MRSW is supported but has the
34   * same semantics as {@link Condition#signal()}, that is to say that
35   * {@link Condition#signalAll()} is not supported and if there are multiple
36   * waiters then the particular thread that is released is arbitrary.
37   */
38  @ThreadSafe
39  public class BooleanLatch implements ReusableLatch {
40      /**
41       * Synchronization control For BooleanLatch. Uses AQS state to represent
42       * released state.
43       */
44      private class Sync extends AbstractQueuedSynchronizer {
45          private static final long serialVersionUID = -3475411235403448115L;
46  
47          private static final int RELEASED = 0;
48          private static final int UNAVAILABLE = -1;
49  
50          private Sync() {
51              setState(UNAVAILABLE);
52          }
53  
54          @Override
55          protected boolean tryAcquire(final int ignore) {
56              final boolean acquired = (getState() == RELEASED);
57              if (acquired) {
58                  compareAndSetState(RELEASED, UNAVAILABLE);
59              }
60              return acquired;
61          }
62  
63          @Override
64          protected boolean tryRelease(final int ignore) {
65              final int state = getState();
66              if (state == UNAVAILABLE) {
67                  setState(RELEASED);
68              }
69              return true;
70          }
71      }
72  
73      private final Sync sync = new Sync();
74  
75      /**
76       * {@inheritDoc}
77       * 
78       * Releases at most one waiting thread. If the current state is released
79       * then nothing happens.
80       */
81      public void release() {
82          sync.release(0);
83      }
84  
85      /**
86       * {@inheritDoc}
87       * 
88       * Causes the current thread to wait until the latch has been released,
89       * unless the thread is {@linkplain Thread#interrupt() interrupted}.
90       * <p>
91       * If the latch has already been released then this method returns
92       * immediately.
93       * <p>
94       * If the latch is not released then the current thread becomes disabled for
95       * thread scheduling purposes and lies dormant until one of two things
96       * happen:
97       * <ul>
98       * <li>The latch is released by another thread invoking the
99       * {@link #release()} method; or
100      * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
101      * current thread.
102      * </ul>
103      * <p>
104      * If the current thread:
105      * <ul>
106      * <li>has its interrupted status set on entry to this method; or
107      * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
108      * </ul>
109      * then {@link InterruptedException} is thrown and the current thread's
110      * interrupted status is cleared.
111      * 
112      * @throws InterruptedException if the current thread is interrupted while
113      * waiting
114      */
115     public void await() throws InterruptedException {
116         sync.acquireInterruptibly(0);
117     }
118 
119     /**
120      * {@inheritDoc}
121      * 
122      * Causes the current thread to wait until the latch has been released,
123      * unless the thread is {@linkplain Thread#interrupt() interrupted}, or the
124      * specified waiting time elapses.
125      * <p>
126      * If the latch has already been released then this method returns
127      * immediately with return value true.
128      * <p>
129      * If the latch is unreleased then the current thread becomes disabled for
130      * thread scheduling purposes and lies dormant until one of three things
131      * happen:
132      * <ul>
133      * <li>The latch is released by another thread invoking the
134      * {@link #release()} method; or
135      * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
136      * current thread; or
137      * <li>The specified waiting time elapses.
138      * </ul>
139      * <p>
140      * If latch is released by another thread then the method returns with the
141      * value {@code true}.
142      * <p>
143      * If the current thread:
144      * <ul>
145      * <li>has its interrupted status set on entry to this method; or
146      * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
147      * </ul>
148      * then {@link InterruptedException} is thrown and the current thread's
149      * interrupted status is cleared.
150      * <p>
151      * If the specified waiting time elapses then the value {@code false} is
152      * returned. If the time is less than or equal to zero, the method will not
153      * wait at all.
154      * 
155      * @param timeout the maximum time to wait
156      * @param unit the time unit of the {@code timeout} argument
157      * @return {@code true} if the count reached zero and {@code false} if the
158      * waiting time elapsed before the count reached zero
159      * @throws InterruptedException if the current thread is interrupted while
160      * waiting
161      */
162     public boolean await(final long timeout, final TimeUnit unit) throws InterruptedException {
163         return sync.tryAcquireNanos(1, unit.toNanos(timeout));
164     }
165 }