@GwtIncompatible("Thread")
public void testSupplierThreadSafe(
Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)
throws Throwable {
final AtomicInteger count = new AtomicInteger(0);
final AtomicReference<Throwable> thrown =
new AtomicReference<Throwable>(null);
final int numThreads = 3;
final Thread[] threads = new Thread[numThreads];
final long timeout = TimeUnit.MINUTES.toNanos(1);
final Supplier<Boolean> supplier = new Supplier<Boolean>() {
boolean isWaiting(Thread thread) {
switch (thread.getState()) {
case BLOCKED:
case WAITING:
case TIMED_WAITING:
return true;
default:
return false;
}
}
int waitingThreads() {
int waitingThreads = 0;
for (Thread thread : threads) {
if (isWaiting(thread)) {
waitingThreads++;
}
}
return waitingThreads;
}
@Override
public Boolean get() {
// Check that this method is called exactly once, by the first
// thread to synchronize.
long t0 = System.nanoTime();
while (waitingThreads() != numThreads - 1) {
if (System.nanoTime() - t0 > timeout) {
thrown.set(new TimeoutException(
"timed out waiting for other threads to block" +
" synchronizing on supplier"));
break;
}
Thread.yield();
}
count.getAndIncrement();
return Boolean.TRUE;
}
};
final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);
for (int i = 0; i < numThreads; i++) {
threads[i] = new Thread() {
@Override public void run() {
assertSame(Boolean.TRUE, memoizedSupplier.get());
}
};
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
if (thrown.get() != null) {
throw thrown.get();
}
assertEquals(1, count.get());
}