blob: 9832c30b029c908d449abfb109d4b3b76b4f598f [file] [log] [blame]
/*
* Copyright (C) 2011 The Guava Authors
*
* 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.google.common.util.concurrent;
import static com.google.common.collect.Iterables.concat;
import com.google.common.base.Functions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.testing.GcFinalization;
import com.google.common.testing.NullPointerTester;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import junit.framework.TestCase;
/**
* Tests for Striped.
*
* @author Dimitris Andreou
*/
public class StripedTest extends TestCase {
private static List<Striped<?>> strongImplementations() {
return ImmutableList.of(
Striped.readWriteLock(100),
Striped.readWriteLock(256),
Striped.lock(100),
Striped.lock(256),
Striped.custom(
100,
new Supplier<Lock>() {
@Override
public Lock get() {
return new ReentrantLock(true);
}
}),
Striped.custom(
256,
new Supplier<Lock>() {
@Override
public Lock get() {
return new ReentrantLock(true);
}
}),
Striped.semaphore(100, 1),
Striped.semaphore(256, 1));
}
private static final Supplier<ReadWriteLock> READ_WRITE_LOCK_SUPPLIER =
new Supplier<ReadWriteLock>() {
@Override
public ReadWriteLock get() {
return new ReentrantReadWriteLock();
}
};
private static final Supplier<Lock> LOCK_SUPPLER =
new Supplier<Lock>() {
@Override
public Lock get() {
return new ReentrantLock();
}
};
private static final Supplier<Semaphore> SEMAPHORE_SUPPLER =
new Supplier<Semaphore>() {
@Override
public Semaphore get() {
return new Semaphore(1, false);
}
};
private static List<Striped<?>> weakImplementations() {
return ImmutableList.<Striped<?>>builder()
.add(new Striped.SmallLazyStriped<ReadWriteLock>(50, READ_WRITE_LOCK_SUPPLIER))
.add(new Striped.SmallLazyStriped<ReadWriteLock>(64, READ_WRITE_LOCK_SUPPLIER))
.add(new Striped.LargeLazyStriped<ReadWriteLock>(50, READ_WRITE_LOCK_SUPPLIER))
.add(new Striped.LargeLazyStriped<ReadWriteLock>(64, READ_WRITE_LOCK_SUPPLIER))
.add(new Striped.SmallLazyStriped<Lock>(50, LOCK_SUPPLER))
.add(new Striped.SmallLazyStriped<Lock>(64, LOCK_SUPPLER))
.add(new Striped.LargeLazyStriped<Lock>(50, LOCK_SUPPLER))
.add(new Striped.LargeLazyStriped<Lock>(64, LOCK_SUPPLER))
.add(new Striped.SmallLazyStriped<Semaphore>(50, SEMAPHORE_SUPPLER))
.add(new Striped.SmallLazyStriped<Semaphore>(64, SEMAPHORE_SUPPLER))
.add(new Striped.LargeLazyStriped<Semaphore>(50, SEMAPHORE_SUPPLER))
.add(new Striped.LargeLazyStriped<Semaphore>(64, SEMAPHORE_SUPPLER))
.build();
}
private static Iterable<Striped<?>> allImplementations() {
return concat(strongImplementations(), weakImplementations());
}
public void testNull() throws Exception {
for (Striped<?> striped : allImplementations()) {
new NullPointerTester().testAllPublicInstanceMethods(striped);
}
}
public void testSizes() {
// not bothering testing all variations, since we know they share implementations
assertTrue(Striped.lock(100).size() >= 100);
assertTrue(Striped.lock(256).size() == 256);
assertTrue(Striped.lazyWeakLock(100).size() >= 100);
assertTrue(Striped.lazyWeakLock(256).size() == 256);
}
public void testWeakImplementations() {
for (Striped<?> striped : weakImplementations()) {
WeakReference<Object> weakRef = new WeakReference<>(striped.get(new Object()));
GcFinalization.awaitClear(weakRef);
}
}
public void testWeakReadWrite() {
Striped<ReadWriteLock> striped = Striped.lazyWeakReadWriteLock(1000);
Object key = new Object();
Lock readLock = striped.get(key).readLock();
WeakReference<Object> garbage = new WeakReference<>(new Object());
GcFinalization.awaitClear(garbage);
Lock writeLock = striped.get(key).writeLock();
readLock.lock();
assertFalse(writeLock.tryLock());
readLock.unlock();
}
public void testStrongImplementations() {
for (Striped<?> striped : strongImplementations()) {
WeakReference<Object> weakRef = new WeakReference<>(striped.get(new Object()));
WeakReference<Object> garbage = new WeakReference<>(new Object());
GcFinalization.awaitClear(garbage);
assertNotNull(weakRef.get());
}
}
public void testMaximalWeakStripedLock() {
Striped<Lock> stripedLock = Striped.lazyWeakLock(Integer.MAX_VALUE);
for (int i = 0; i < 10000; i++) {
stripedLock.get(new Object()).lock();
// nothing special (e.g. an exception) happens
}
}
public void testBulkGetReturnsSorted() {
for (Striped<?> striped : allImplementations()) {
Map<Object, Integer> indexByLock = Maps.newHashMap();
for (int i = 0; i < striped.size(); i++) {
indexByLock.put(striped.getAt(i), i);
}
// ensure that bulkGet returns locks in monotonically increasing order
for (int objectsNum = 1; objectsNum <= striped.size() * 2; objectsNum++) {
Set<Object> objects = Sets.newHashSetWithExpectedSize(objectsNum);
for (int i = 0; i < objectsNum; i++) {
objects.add(new Object());
}
Iterable<?> locks = striped.bulkGet(objects);
assertTrue(Ordering.natural().onResultOf(Functions.forMap(indexByLock)).isOrdered(locks));
// check idempotency
Iterable<?> locks2 = striped.bulkGet(objects);
assertEquals(Lists.newArrayList(locks), Lists.newArrayList(locks2));
}
}
}
/** Checks idempotency, and that we observe the promised number of stripes. */
public void testBasicInvariants() {
for (Striped<?> striped : allImplementations()) {
assertBasicInvariants(striped);
}
}
private static void assertBasicInvariants(Striped<?> striped) {
Set<Object> observed = Sets.newIdentityHashSet(); // for the sake of weakly referenced locks.
// this gets the stripes with #getAt(index)
for (int i = 0; i < striped.size(); i++) {
Object object = striped.getAt(i);
assertNotNull(object);
assertSame(object, striped.getAt(i)); // idempotent
observed.add(object);
}
assertTrue("All stripes observed", observed.size() == striped.size());
// this uses #get(key), makes sure an already observed stripe is returned
for (int i = 0; i < striped.size() * 100; i++) {
assertTrue(observed.contains(striped.get(new Object())));
}
try {
striped.getAt(-1);
fail();
} catch (RuntimeException expected) {
}
try {
striped.getAt(striped.size());
fail();
} catch (RuntimeException expected) {
}
}
public void testMaxSize() {
for (Striped<?> striped :
ImmutableList.of(
Striped.lazyWeakLock(Integer.MAX_VALUE),
Striped.lazyWeakSemaphore(Integer.MAX_VALUE, Integer.MAX_VALUE),
Striped.lazyWeakReadWriteLock(Integer.MAX_VALUE))) {
for (int i = 0; i < 3; i++) {
// doesn't throw exception
Object unused = striped.getAt(Integer.MAX_VALUE - i);
}
}
}
}