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