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 }