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 }