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