blob: a0a923ef76fef2091c4283c24c02e68a963f5e2d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.utility.tests.internal.collection;
import java.util.EmptyStackException;
import org.eclipse.jpt.common.utility.collection.Stack;
import org.eclipse.jpt.common.utility.internal.collection.LinkedStack;
import org.eclipse.jpt.common.utility.internal.collection.SynchronizedStack;
@SuppressWarnings("nls")
public class SynchronizedStackTests
extends StackTests
{
private volatile SynchronizedStack<String> ss;
volatile boolean timeoutOccurred;
volatile long startTime;
volatile long endTime;
volatile Object poppedObject;
static final String ITEM_1 = new String();
static final String ITEM_2 = new String();
public SynchronizedStackTests(String name) {
super(name);
}
@Override
Stack<String> buildStack() {
return new SynchronizedStack<String>();
}
@Override
public void testClone() {
// synchronized stack is not cloneable
}
@Override
protected void setUp() throws Exception {
super.setUp();
this.ss = new SynchronizedStack<String>();
this.timeoutOccurred = false;
this.startTime = 0;
this.endTime = 0;
this.poppedObject = null;
}
/**
* test first with an unsynchronized stack,
* then with a synchronized stack
*/
public void testConcurrentAccess() throws Exception {
this.verifyConcurrentAccess(new SlowLinkedStack<String>(), "second");
this.verifyConcurrentAccess(new SlowSynchronizedStack<String>(), "first");
}
private void verifyConcurrentAccess(SlowStack<String> slowStack, String expected) throws Exception {
slowStack.push("first");
slowStack.push("second");
Thread thread = this.buildThread(this.buildRunnable(slowStack));
thread.start();
Thread.sleep(TWO_TICKS);
assertEquals(expected, slowStack.pop());
thread.join();
assertTrue(slowStack.isEmpty());
}
private Runnable buildRunnable(final SlowStack<String> slowStack) {
return new Runnable() {
public void run() {
slowStack.slowPop();
}
};
}
private interface SlowStack<E> extends Stack<E> {
Object slowPop();
}
private class SlowLinkedStack<E> extends LinkedStack<E> implements SlowStack<E> {
private static final long serialVersionUID = 1L;
SlowLinkedStack() {
super();
}
public Object slowPop() {
try {
Thread.sleep(5 * TICK);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return this.pop();
}
}
private class SlowSynchronizedStack<E> extends SynchronizedStack<E> implements SlowStack<E> {
private static final long serialVersionUID = 1L;
SlowSynchronizedStack() {
super();
}
public synchronized Object slowPop() {
try {
Thread.sleep(5 * TICK);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return this.pop();
}
}
// ********** waits **********
public void testWaitToPop() throws Exception {
this.verifyWaitToPop(0);
// no timeout occurs...
assertFalse(this.timeoutOccurred);
// ...and an item should have been popped by t2...
assertSame(ITEM_1, this.poppedObject);
// ...and the stack should be empty
assertTrue(this.ss.isEmpty());
// make a reasonable guess about how long t2 took
assertTrue(this.calculateElapsedTime() > TICK);
}
public void testWaitToPopTimeout() throws Exception {
this.verifyWaitToPop(TICK);
// timeout occurs...
assertTrue(this.timeoutOccurred);
// ...and the stack was never popped...
assertNull(this.poppedObject);
// ...and it still holds the item
assertSame(ITEM_1, this.ss.peek());
// make a reasonable guess about how long t2 took
assertTrue(this.calculateElapsedTime() < THREE_TICKS);
}
private void verifyWaitToPop(long timeout) throws Exception {
Runnable r1 = this.buildRunnable(this.buildPushCommand(), this.ss, TWO_TICKS);
Runnable r2 = this.buildRunnable(this.buildWaitToPopCommand(timeout), this.ss, 0);
Thread t1 = this.buildThread(r1);
Thread t2 = this.buildThread(r2);
t1.start();
t2.start();
t1.join();
t2.join();
}
public void testWaitToPush() throws Exception {
this.verifyWaitToPush(0);
// no timeout occurs...
assertFalse(this.timeoutOccurred);
// ...and the stack gets popped by t1...
assertSame(ITEM_1, this.poppedObject);
// ...and an item is pushed on to the stack by t2
assertFalse(this.ss.isEmpty());
assertSame(ITEM_2, this.ss.peek());
// make a reasonable guess about how long t2 took
assertTrue(this.calculateElapsedTime() > TICK);
}
public void testWaitToPushTimeout() throws Exception {
this.verifyWaitToPush(TICK);
// timeout occurs...
assertTrue(this.timeoutOccurred);
// ...and the stack is eventually popped by t1...
assertSame(ITEM_1, this.poppedObject);
// ...but nothing is pushed on to the stack by t2
assertTrue(this.ss.isEmpty());
// make a reasonable guess about how long t2 took
assertTrue(this.calculateElapsedTime() < THREE_TICKS);
}
private void verifyWaitToPush(long timeout) throws Exception {
this.ss.push(ITEM_1);
Runnable r1 = this.buildRunnable(this.buildPopCommand(), this.ss, TWO_TICKS);
Runnable r2 = this.buildRunnable(this.buildWaitToPushCommand(timeout), this.ss, 0);
Thread t1 = this.buildThread(r1);
Thread t2 = this.buildThread(r2);
t1.start();
t2.start();
t1.join();
t2.join();
}
private Command buildPushCommand() {
return new Command() {
public void execute(SynchronizedStack<String> synchronizedStack) {
synchronizedStack.push(ITEM_1);
}
};
}
private Command buildWaitToPopCommand(final long timeout) {
return new Command() {
public void execute(SynchronizedStack<String> synchronizedStack) throws InterruptedException {
SynchronizedStackTests.this.startTime = System.currentTimeMillis();
try {
SynchronizedStackTests.this.poppedObject = synchronizedStack.waitToPop(timeout);
} catch (EmptyStackException ex) {
SynchronizedStackTests.this.timeoutOccurred = true;
}
SynchronizedStackTests.this.endTime = System.currentTimeMillis();
}
};
}
private Command buildPopCommand() {
return new Command() {
public void execute(SynchronizedStack<String> synchronizedStack) {
SynchronizedStackTests.this.poppedObject = synchronizedStack.pop();
}
};
}
private Command buildWaitToPushCommand(final long timeout) {
return new Command() {
public void execute(SynchronizedStack<String> synchronizedStack) throws InterruptedException {
SynchronizedStackTests.this.startTime = System.currentTimeMillis();
SynchronizedStackTests.this.timeoutOccurred = ! synchronizedStack.waitToPush(ITEM_2, timeout);
SynchronizedStackTests.this.endTime = System.currentTimeMillis();
}
};
}
private Runnable buildRunnable(final Command command, final SynchronizedStack<String> synchronizedStack, final long sleep) {
return new TestRunnable() {
@Override
protected void run_() throws Throwable {
if (sleep != 0) {
Thread.sleep(sleep);
}
command.execute(synchronizedStack);
}
};
}
long calculateElapsedTime() {
return this.endTime - this.startTime;
}
// ********** Command interface **********
private interface Command {
void execute(SynchronizedStack<String> synchronizedStack) throws InterruptedException;
}
}