/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Oct 1, 2007
*/
package com.bigdata.concurrent;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
import com.bigdata.concurrent.NonBlockingLockManagerWithNewDesign.LockFutureTask;
import com.bigdata.util.DaemonThreadFactory;
/**
* basic unit tests.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestNonBlockingLockManagerWithNewDesign extends TestCase {
protected static final Logger log = Logger
.getLogger(TestNonBlockingLockManagerWithNewDesign.class);
protected static final boolean INFO = log.isInfoEnabled();
protected static final boolean DEBUG = log.isDebugEnabled();
/**
*
*/
public TestNonBlockingLockManagerWithNewDesign() {
super();
}
public TestNonBlockingLockManagerWithNewDesign(String name) {
super(name);
}
/**
* Waits 10ms once it acquires its locks.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public static class Wait10ResourceTask<T> implements Callable<T> {
public T call() throws Exception {
// if (INFO)
// log.info("Executing: "+this);
synchronized (this) {
try {
if (DEBUG)
log.debug("Waiting: "+this);
wait(10/* milliseconds */);
// if (INFO)
// log.info("Done waiting: "+this);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// if (INFO)
// log.info("Done: "+this);
return null;
}
}
/**
* Dies once it acquires its locks by throwing {@link HorridTaskDeath}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
static class DeathResourceTask<T> implements Callable<T> {
public T call() throws Exception {
if(DEBUG)
log.debug("Arrgh!");
throw new HorridTaskDeath();
}
}
/**
* Test startup and fast shutdown.
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test_shutdownNow() throws InterruptedException, ExecutionException {
final NonBlockingLockManagerWithNewDesign<String> service = new NonBlockingLockManagerWithNewDesign<String>(
10/* maxConcurrency */, 1/* maxLockTries */,
true/* predeclareLocks */) {
protected void ready(Runnable r) {
throw new UnsupportedOperationException();
}
};
try {
assertTrue(service.isOpen());
} finally {
assertFalse(service.isShutdown());
service.shutdownNow();
assertTrue(service.isShutdown());
}
}
/**
* Test startup and normal shutdown.
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test_shutdown() throws InterruptedException, ExecutionException {
final NonBlockingLockManagerWithNewDesign<String> service = new NonBlockingLockManagerWithNewDesign<String>(
10/* maxConcurrency */, 1/* maxLockTries */,
true/* predeclareLocks */) {
protected void ready(Runnable r) {
throw new UnsupportedOperationException();
}
};
try {
assertTrue(service.isOpen());
} finally {
assertFalse(service.isShutdown());
service.shutdown();
assertTrue(service.isShutdown());
}
}
/**
* Create an {@link Executor}. The caller is responsible for shutting down
* the service.
*
* @return The {@link Executor}.
*/
private ExecutorService newExecutor() {
final int corePoolSize = 10;
final int maximumPoolSize = 10;
final long keepAliveTime = 60;
final TimeUnit unit = TimeUnit.SECONDS;
final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();
return new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, unit, workQueue, new DaemonThreadFactory(
getClass().getName()));
}
/**
* Test ability to submit a {@link Callable} to the service and verify that
* is reported at ready(Runnable) and that we can cancel the {@link Future}.
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test_submitOneThenCancel() throws InterruptedException, ExecutionException {
final BlockingQueue<Runnable> readyQueue = new LinkedBlockingQueue<Runnable>();
final NonBlockingLockManagerWithNewDesign<String> service = new NonBlockingLockManagerWithNewDesign<String>(
10/* maxConcurrency */, 1/* maxLockTries */,
true/* predeclareLocks */) {
protected void ready(Runnable r) {
readyQueue.add(r);
}
};
try {
final String expected = "a";
final LockFutureTask<String,String> f = (LockFutureTask<String,String>) service
.submit(new String[0], new Callable<String>() {
public String call() throws Exception {
return expected;
}
});
/*
* Note: This verifies that locks are granted synchronously when we
* accept a task whose locks are not already held by someone else.
*/
assertEquals(1, readyQueue.size());
assertEquals(f, readyQueue.peek());
assertFalse(f.isCancelled());
assertFalse(f.isDone());
assertEquals(
NonBlockingLockManagerWithNewDesign.TaskRunState.LocksReady,
f.getTaskRunState());
f.cancel(true/* mayInterruptIfRunning */);
assertTrue(f.isCancelled());
assertTrue(f.isDone());
assertEquals(
NonBlockingLockManagerWithNewDesign.TaskRunState.Halted,
f.getTaskRunState());
try {
f.get();
fail("Expecting: " + CancellationException.class);
} catch (CancellationException ex) {
if (INFO)
log.info("Expected exception: " + ex);
}
} finally {
service.shutdownNow();
}
}
/**
* Test ability to run a {@link Callable} on the service, get() the result,
* and then shutdown the service.
*
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public void test_runOne() throws InterruptedException, ExecutionException,
TimeoutException {
final ExecutorService delegate = newExecutor();
final NonBlockingLockManagerWithNewDesign<String> service = new NonBlockingLockManagerWithNewDesign<String>(
10/* maxConcurrency */, 1/* maxLockTries */, true/* predeclareLocks */) {
protected void ready(Runnable r) {
delegate.execute(r);
}
};
try {
final String expected = "a";
final LockFutureTask<String, String> f = (LockFutureTask<String, String>) service
.submit(new String[0], new Callable<String>() {
public String call() throws Exception {
return expected;
}
});
assertFalse(f.isCancelled());
// Note: Set unit to HOURS for debugging :-)
assertEquals(expected, f.get(10/* ms */, TimeUnit.MILLISECONDS));
/*
* Note: LockFutureTask can not be made to reliably release the
* locks and update the run state before Future#get() returns.
* Therefore this test sleeps a bit before checking the run state.
*/
Thread.sleep(10/*ms*/);
// verify that the run state was updated.
assertEquals(
NonBlockingLockManagerWithNewDesign.TaskRunState.Halted, f
.getTaskRunState());
} finally {
service.shutdownNow();
delegate.shutdownNow();
}
}
/**
* Test ability to run a {@link Callable} on the service which throws an
* exception, get() the result, and then shutdown the service.
*
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public void test_runOneThrowsException() throws InterruptedException, ExecutionException,
TimeoutException {
final ExecutorService delegate = newExecutor();
final NonBlockingLockManagerWithNewDesign<String> service = new NonBlockingLockManagerWithNewDesign<String>(
10/* maxConcurrency */, 1/* maxLockTries */, true/* predeclareLocks */) {
protected void ready(Runnable r) {
delegate.execute(r);
}
};
try {
final LockFutureTask<String, String> f = (LockFutureTask<String, String>) service
.submit(new String[0], new Callable<String>() {
public String call() throws Exception {
// task throws exception.
throw new HorridTaskDeath();
}
});
assertFalse(f.isCancelled());
try {
// Note: Set unit to HOURS for debugging :-)
f.get(10/* ms */, TimeUnit.MILLISECONDS);
fail("Expecting: "+HorridTaskDeath.class);
} catch (ExecutionException ex) {
if (ex.getCause() instanceof HorridTaskDeath) {
if(INFO)
log.info("Ignoring expected exception: " + ex);
} else {
final AssertionFailedError err = new AssertionFailedError(
"Expecting: " + HorridTaskDeath.class
+ " as the cause");
err.initCause(ex);
throw err;
}
}
} finally {
service.shutdownNow();
delegate.shutdownNow();
}
}
/**
* Succeeds if the task holds all of its declared locks.
* @param <R>
* @param <T>
* @param service
* @param task
*/
protected <R extends Comparable<R>, T> void assertLocksHeld(
final NonBlockingLockManagerWithNewDesign<R> service,
final LockFutureTask<R, T> task) {
for (R r : task.getResource()) {
if (!service.isLockHeldByTask(r, task)) {
fail("Task does not hold lock: " + task);
}
}
}
/**
* Succeeds if the task holds none of its declared locks.
* @param <R>
* @param <T>
* @param service
* @param task
*/
protected <R extends Comparable<R>, T> void assertLocksNotHeld(
final NonBlockingLockManagerWithNewDesign<R> service,
final LockFutureTask<R, T> task) {
for(R r : task.getResource()) {
if(service.isLockHeldByTask(r, task)) {
fail("Task holds lock: "+task);
}
}
}
/**
* Test ability to obtain a lock, run a {@link Callable} on the service,
* get() the result, and then shutdown the service.
*
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public void test_runOneWithLock() throws InterruptedException,
ExecutionException, TimeoutException {
final ExecutorService delegate = newExecutor();
final NonBlockingLockManagerWithNewDesign<String> service = new NonBlockingLockManagerWithNewDesign<String>(
10/* maxConcurrency */, 1/* maxLockTries */, true/* predeclareLocks */) {
protected void ready(Runnable r) {
delegate.execute(r);
}
};
try {
final String expected = "a";
final LockFutureTask<String, String> f = (LockFutureTask<String, String>) service
.submit(new String[] { "test" }, new Callable<String>() {
public String call() throws Exception {
return expected;
}
});
assertEquals(expected, f.get(10/* ms */, TimeUnit.MILLISECONDS));
/*
* Note: LockFutureTask can not be made to reliably release the
* locks and update the run state before Future#get() returns.
* Therefore this test sleeps a bit before verifying that the locks
* were released.
*/
Thread.sleep(10/*ms*/);
assertLocksNotHeld(service, f);
} finally {
service.shutdownNow();
delegate.shutdownNow();
}
}
/**
* Test ability to obtain a lock, run a {@link Callable} on the service that
* releases its locks during its computation, verify that the locks were
* released, get() the result, and then shutdown the service.
*
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public void test_runOneWithLockAndReleaseLockFromTask() throws InterruptedException,
ExecutionException, TimeoutException {
final ExecutorService delegate = newExecutor();
final NonBlockingLockManagerWithNewDesign<String> service = new NonBlockingLockManagerWithNewDesign<String>(
10/* maxConcurrency */, 1/* maxLockTries */, true/* predeclareLocks */) {
protected void ready(Runnable r) {
// verify locks are held.
assertLocksHeld(this, (LockFutureTask<String, String>) r);
delegate.execute(r);
}
};
try {
final String expected = "a";
final LockFutureTask<String, String> f = (LockFutureTask<String, String>) service
.submit(new String[] { "test" }, new Callable<String>() {
public String call() throws Exception {
// release locks.
service
.releaseLocksForTask(new String[] { "test" });
try {
// error expected the 2nd time.
service
.releaseLocksForTask(new String[] { "test" });
fail("Expected: " + IllegalStateException.class);
} catch (IllegalStateException ex) {
if (INFO)
log.info("Ignoring expected exception: "
+ ex);
}
return expected;
}
});
assertEquals(expected, f.get(10/* ms */, TimeUnit.MILLISECONDS));
/*
* Note: LockFutureTask can not be made to reliably release the
* locks and update the run state before Future#get() returns.
* Therefore this test sleeps a bit before verifying that the locks
* were released.
*/
Thread.sleep(10/*ms*/);
assertLocksNotHeld(service, f);
} finally {
service.shutdownNow();
delegate.shutdownNow();
}
}
}