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 java.util.concurrent.TimeUnit;
20  import java.util.concurrent.locks.AbstractQueuedSynchronizer;
21  import java.util.concurrent.locks.Condition;
22  
23  import net.jcip.annotations.ThreadSafe;
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 static 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              if (!(getState() == RELEASED)) {
57                  return false;
58              }
59              return compareAndSetState(RELEASED, UNAVAILABLE);
60          }
61  
62          @Override
63          protected boolean tryRelease(final int ignore) {
64              final int state = getState();
65              if (state == UNAVAILABLE) {
66                  setState(RELEASED);
67              }
68              return true;
69          }
70      }
71  
72      private final Sync sync = new Sync();
73  
74      /**
75       * {@inheritDoc}
76       * 
77       * Releases at most one waiting thread. If the current state is released
78       * then nothing happens.
79       */
80      public void release() {
81          sync.release(0);
82      }
83  
84      /**
85       * {@inheritDoc}
86       * 
87       * Causes the current thread to wait until the latch has been released,
88       * unless the thread is {@linkplain Thread#interrupt() interrupted}.
89       * <p>
90       * If the latch has already been released then this method returns
91       * immediately.
92       * <p>
93       * If the latch is not released then the current thread becomes disabled for
94       * thread scheduling purposes and lies dormant until one of two things
95       * happen:
96       * <ul>
97       * <li>The latch is released by another thread invoking the
98       * {@link #release()} method; or
99       * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
100      * current thread.
101      * </ul>
102      * <p>
103      * If the current thread:
104      * <ul>
105      * <li>has its interrupted status set on entry to this method; or
106      * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
107      * </ul>
108      * then {@link InterruptedException} is thrown and the current thread's
109      * interrupted status is cleared.
110      * 
111      * @throws InterruptedException if the current thread is interrupted while
112      * waiting
113      */
114     public void await() throws InterruptedException {
115         sync.acquireInterruptibly(0);
116     }
117 
118     /**
119      * {@inheritDoc}
120      * 
121      * Causes the current thread to wait until the latch has been released,
122      * unless the thread is {@linkplain Thread#interrupt() interrupted}, or the
123      * specified waiting time elapses.
124      * <p>
125      * If the latch has already been released then this method returns
126      * immediately with return value true.
127      * <p>
128      * If the latch is unreleased then the current thread becomes disabled for
129      * thread scheduling purposes and lies dormant until one of three things
130      * happen:
131      * <ul>
132      * <li>The latch is released by another thread invoking the
133      * {@link #release()} method; or
134      * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
135      * current thread; or
136      * <li>The specified waiting time elapses.
137      * </ul>
138      * <p>
139      * If latch is released by another thread then the method returns with the
140      * value {@code true}.
141      * <p>
142      * If the current thread:
143      * <ul>
144      * <li>has its interrupted status set on entry to this method; or
145      * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
146      * </ul>
147      * then {@link InterruptedException} is thrown and the current thread's
148      * interrupted status is cleared.
149      * <p>
150      * If the specified waiting time elapses then the value {@code false} is
151      * returned. If the time is less than or equal to zero, the method will not
152      * wait at all.
153      * 
154      * @param timeout the maximum time to wait
155      * @param unit the time unit of the {@code timeout} argument
156      * @return {@code true} if the count reached zero and {@code false} if the
157      * waiting time elapsed before the count reached zero
158      * @throws InterruptedException if the current thread is interrupted while
159      * waiting
160      */
161     public boolean await(final long timeout, final TimeUnit unit) throws InterruptedException {
162         return sync.tryAcquireNanos(0, unit.toNanos(timeout));
163     }
164 }