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 and waited on. It
27   * depends on a boolean condition of being released or not and becomes unreleased when one thread
28   * successfully awaits it. It is useful for rally like release-wait-release coordination, and as a
29   * replacement to waiting on a {@link Condition} (it should be faster as the write thread does not
30   * need to acquire a lock in order to signal.
31   * <p>
32   * This latch is suitable for SRSW coordination. MRSW is supported but has the same semantics as
33   * {@link Condition#signal()}, that is to say that {@link Condition#signalAll()} is not supported
34   * and if there are multiple waiters then the particular thread that is released is arbitrary.
35   */
36  @ThreadSafe public class BooleanLatch {
37      /**
38       * Synchronization control For BooleanLatch. Uses AQS state to represent released state.
39       */
40      private class Sync extends AbstractQueuedSynchronizer {
41          private static final long serialVersionUID = -3475411235403448115L;
42  
43          private static final int RELEASED = 0;
44          private static final int UNAVAILABLE = -1;
45  
46          private Sync() {
47              setState(UNAVAILABLE);
48          }
49  
50          @Override protected boolean tryAcquire(final int ignore) {
51              final boolean acquired = (getState() == RELEASED);
52              if (acquired) {
53                  compareAndSetState(RELEASED, UNAVAILABLE);
54              }
55              return acquired;
56          }
57  
58          @Override protected boolean tryRelease(final int ignore) {
59              final int state = getState();
60              if (state == UNAVAILABLE) {
61                  setState(RELEASED);
62              }
63              return true;
64          }
65      }
66  
67      private final Sync sync = new Sync();
68  
69      /**
70       * Release the latch, releasing at most one waiting thread.
71       * <p>
72       * If the current state is released then nothing happens.
73       */
74      public void release() {
75          sync.release(0);
76      }
77  
78      /**
79       * Causes the current thread to wait until the latch has been released, unless the thread is
80       * {@linkplain Thread#interrupt interrupted}.
81       * <p>
82       * If the latch has already been released then this method returns immediately.
83       * <p>
84       * If the latch is not released then the current thread becomes disabled for thread scheduling
85       * purposes and lies dormant until one of two things happen:
86       * <ul>
87       * <li>The latch is released by another thread invoking the {@link #release()} method; or
88       * <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread.
89       * </ul>
90       * <p>
91       * If the current thread:
92       * <ul>
93       * <li>has its interrupted status set on entry to this method; or
94       * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
95       * </ul>
96       * then {@link InterruptedException} is thrown and the current thread's interrupted status is
97       * cleared.
98       * 
99       * @throws InterruptedException if the current thread is interrupted while waiting
100      */
101     public void await() throws InterruptedException {
102         sync.acquireInterruptibly(0);
103     }
104 
105     /**
106      * Causes the current thread to wait until the latch has been released, unless the thread is
107      * {@linkplain Thread#interrupt interrupted}, or the specified waiting time elapses.
108      * <p>
109      * If the latch has already been released then this method returns immediately with return value
110      * true.
111      * <p>
112      * If the latch is unreleased then the current thread becomes disabled for thread scheduling
113      * purposes and lies dormant until one of three things happen:
114      * <ul>
115      * <li>The latch is released by another thread invoking the {@link #release()} method; or
116      * <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread; or
117      * <li>The specified waiting time elapses.
118      * </ul>
119      * <p>
120      * If latch is released by another thread then the method returns with the value {@code true}.
121      * <p>
122      * If the current thread:
123      * <ul>
124      * <li>has its interrupted status set on entry to this method; or
125      * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
126      * </ul>
127      * then {@link InterruptedException} is thrown and the current thread's interrupted status is
128      * cleared.
129      * <p>
130      * If the specified waiting time elapses then the value {@code false} is returned. If the time
131      * is less than or equal to zero, the method will not wait at all.
132      * 
133      * @param timeout the maximum time to wait
134      * @param unit the time unit of the {@code timeout} argument
135      * @return {@code true} if the count reached zero and {@code false} if the waiting time elapsed
136      *         before the count reached zero
137      * @throws InterruptedException if the current thread is interrupted while waiting
138      */
139     public boolean await(final long timeout, final TimeUnit unit) throws InterruptedException {
140         return sync.tryAcquireNanos(1, unit.toNanos(timeout));
141     }
142 }