Skip to content

Commit 62f5d82

Browse files
baishengzcopybara-github
authored andcommitted
Internal change
PiperOrigin-RevId: 710914045
1 parent 48fd86c commit 62f5d82

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

src/java/com/google/devtools/mobileharness/shared/util/concurrent/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ package(
2222
],
2323
)
2424

25+
java_library(
26+
name = "barrier",
27+
srcs = ["Barrier.java"],
28+
deps = [
29+
"@maven//:com_google_errorprone_error_prone_annotations",
30+
"@maven//:com_google_guava_guava",
31+
],
32+
)
33+
2534
java_library(
2635
name = "callables",
2736
srcs = ["Callables.java"],
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2022 Google LLC
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+
* https://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.google.devtools.mobileharness.shared.util.concurrent;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
21+
import com.google.errorprone.annotations.concurrent.GuardedBy;
22+
import java.util.concurrent.locks.Condition;
23+
import java.util.concurrent.locks.ReentrantLock;
24+
25+
/**
26+
* A synchronization aid that allows a set of threads to all wait for each other to reach a common
27+
* barrier point. It's similar to {@link java.util.concurrent.CyclicBarrier}, while it supports
28+
* waking up waiting threads before all threads reach the barrier point and let later threads skip
29+
* the barrier.
30+
*/
31+
public class Barrier {
32+
33+
/** The lock for guarding barrier entry */
34+
private final ReentrantLock lock = new ReentrantLock();
35+
36+
/** Condition to wait on until tripped */
37+
private final Condition trip = lock.newCondition();
38+
39+
/** The number of parties */
40+
private final int parties;
41+
42+
/** Number of parties still waiting. Counts down from parties to 0. */
43+
@GuardedBy("lock")
44+
private int count;
45+
46+
/** Whether to stop later awaits. */
47+
@GuardedBy("lock")
48+
private boolean awaitationStopped;
49+
50+
/**
51+
* Creates a new {@code Barrier} that will trip when the given number of parties (threads) are
52+
* waiting upon it, or when {@link #stopAwaitations()} is called.
53+
*
54+
* @param parties the number of threads that must invoke {@link #await()} before the barrier is
55+
* tripped
56+
* @throws IllegalArgumentException if {@code parties} is less than 1
57+
*/
58+
public Barrier(int parties) {
59+
checkArgument(parties > 0);
60+
this.parties = parties;
61+
this.count = parties;
62+
}
63+
64+
/**
65+
* Waits until all {@code parties} have invoked {@link #await()} on this barrier, or {@link
66+
* #stopAwaitations()} is called, or any waiting threads are interrupted which will stop other
67+
* waiting threads.
68+
*
69+
* <p>Later calls to {@link #await()} will return immediately if {@link #stopAwaitations()} was
70+
* called before, or any awaiting threads were interrupted.
71+
*
72+
* @return {@code true} if all parties have invoked {@link #await()} on this barrier, otherwise
73+
* {@code false} if any parties have not invoked {@link #await()} on this barrier (e.g.,
74+
* {@link #stopAwaitations()} is called, or any waiting threads are interrupted).
75+
*/
76+
public boolean await() throws InterruptedException {
77+
lock.lock();
78+
try {
79+
if (count == 0) {
80+
return true;
81+
}
82+
if (awaitationStopped) {
83+
return count <= 0;
84+
}
85+
86+
--count;
87+
// If the current thread is the last one to arrive
88+
if (count == 0) {
89+
trip.signalAll();
90+
return true;
91+
}
92+
93+
// If the current thread is not the last one to arrive, current thread will wait.
94+
while (count > 0) {
95+
try {
96+
trip.await();
97+
} catch (InterruptedException ie) {
98+
stopAwaitations();
99+
throw ie;
100+
}
101+
if (awaitationStopped) {
102+
break;
103+
}
104+
}
105+
106+
return count <= 0;
107+
} finally {
108+
lock.unlock();
109+
}
110+
}
111+
112+
/** Notifies all waiting threads on this barrier and stops later awaits. */
113+
public void stopAwaitations() {
114+
lock.lock();
115+
try {
116+
this.awaitationStopped = true;
117+
trip.signalAll();
118+
} finally {
119+
lock.unlock();
120+
}
121+
}
122+
123+
/**
124+
* Returns the number of parties required to trip this barrier.
125+
*
126+
* @return the number of parties required to trip this barrier
127+
*/
128+
public int getParties() {
129+
return parties;
130+
}
131+
}

0 commit comments

Comments
 (0)