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 }