aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java
diff options
context:
space:
mode:
authorGravatar nharmata <nharmata@google.com>2018-08-13 16:11:16 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-08-13 16:12:22 -0700
commitce170540ba5401e926a5433e6e35b1d22426e525 (patch)
tree01e63404c573168d3793200b935193089acaf591 /src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java
parent74321cffb64adbecb7dff11c273a8d86b70ad210 (diff)
Fix MultisetSemaphore.
We use a fixed version of the previous algorithm. See the comments for details. Fancier algorithms exist. I thought of a cool one that makes use of BatchKeyedLocker (would give me an excuse to revive it, heh), but fancy algorithms would be overkill. As noted in the initial commit of NaiveMultisetSemaphore, performance isn't critical. RELNOTES: None PiperOrigin-RevId: 208560559
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java48
1 files changed, 34 insertions, 14 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java b/src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java
index 8a0bb93bde..94611b5ef3 100644
--- a/src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java
+++ b/src/main/java/com/google/devtools/build/lib/concurrent/MultisetSemaphore.java
@@ -146,30 +146,50 @@ public abstract class MultisetSemaphore<T> {
@Override
public void acquireAll(Set<T> valuesToAcquire) throws InterruptedException {
- int numUniqueValuesToAcquire = 0;
+ int oldNumNeededPermits;
synchronized (lock) {
- for (T value : valuesToAcquire) {
- int oldCount = actualValues.add(value, 1);
- if (oldCount == 0) {
- numUniqueValuesToAcquire++;
+ oldNumNeededPermits = computeNumNeededPermitsLocked(valuesToAcquire);
+ }
+ while (true) {
+ semaphore.acquire(oldNumNeededPermits);
+ synchronized (lock) {
+ int newNumNeededPermits = computeNumNeededPermitsLocked(valuesToAcquire);
+ if (newNumNeededPermits != oldNumNeededPermits) {
+ // While we were doing 'acquire' above, another thread won the race to acquire the first
+ // usage of one of the values in 'valuesToAcquire' or release the last usage of one of
+ // the values. This means we either acquired too many or too few permits, respectively,
+ // above. Release the permits we did acquire, in order to restore the accuracy of the
+ // semaphore's current count, and then try again.
+ semaphore.release(oldNumNeededPermits);
+ oldNumNeededPermits = newNumNeededPermits;
+ continue;
+ } else {
+ // Our modification to the semaphore was correct, so it's sound to update the multiset.
+ valuesToAcquire.forEach(actualValues::add);
+ return;
}
}
}
- semaphore.acquire(numUniqueValuesToAcquire);
+ }
+
+ private int computeNumNeededPermitsLocked(Set<T> valuesToAcquire) {
+ // We need a permit for each value that is not already in the multiset.
+ return (int) valuesToAcquire.stream()
+ .filter(v -> actualValues.count(v) == 0)
+ .count();
}
@Override
public void releaseAll(Set<T> valuesToRelease) {
- int numUniqueValuesToRelease = 0;
synchronized (lock) {
- for (T value : valuesToRelease) {
- int oldCount = actualValues.remove(value, 1);
- if (oldCount == 1) {
- numUniqueValuesToRelease++;
- }
- }
+ // We need to release a permit for each value that currently has multiplicity 1.
+ int numPermitsToRelease =
+ valuesToRelease
+ .stream()
+ .mapToInt(v -> actualValues.remove(v, 1) == 1 ? 1 : 0)
+ .sum();
+ semaphore.release(numPermitsToRelease);
}
- semaphore.release(numUniqueValuesToRelease);
}
@Override