/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.concurrency;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Naive implementation of blocking set: class that allows {@link #put(Object)} lock by the {@code key} specified and
* {@link #remove(Object)} it. The main feature is that another invocation of {@link #put(Object)} with the same {@code key} could not
* be proceeded while one won't call {@link #remove(Object)} with this {@code key}.
*
* @author Alexander Koshevoy
*/
public class BlockingSet<T> {
private final Set<T> set;
private final Condition unlock;
private final Lock lock;
public BlockingSet() {
set = new HashSet<T>();
lock = new ReentrantLock();
unlock = lock.newCondition();
}
/**
* Acquires lock by {@code key}. If lock by {@code key} has been already acquired wait until it is released. Acquire is <b>not</b> reentrant.
*/
public void put(@NotNull T key) {
lock.lock();
try {
while (set.contains(key)) {
unlock.awaitUninterruptibly();
}
set.add(key);
}
finally {
lock.unlock();
}
}
/**
* Releases lock by {@code key}. If lock has not been acquired throws {@link IllegalStateException}.
*
* @throws IllegalStateException if lock by {@code key} has not been acquired.
*/
public void remove(@NotNull T key) throws IllegalStateException {
lock.lock();
try {
if (!set.contains(key)) {
throw new IllegalStateException();
}
set.remove(key);
unlock.signalAll();
}
finally {
lock.unlock();
}
}
public static <T> BlockingSet<T> newInstance() {
return new BlockingSet<T>();
}
}