1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/samples/deadlock/src/org/apidesign/javamonitorflaws/Cache.java Tue Feb 10 18:36:21 2009 +0100
1.3 @@ -0,0 +1,40 @@
1.4 +package org.apidesign.javamonitorflaws;
1.5 +
1.6 +import java.util.HashMap;
1.7 +import java.util.Map;
1.8 +
1.9 +/**
1.10 + *
1.11 + * @author Jaroslav Tulach <jtulach@netbeans.org>
1.12 + */
1.13 +public abstract class Cache<From,To> {
1.14 + private Map<From,To> cache;
1.15 +
1.16 + public final To get(From f) {
1.17 + for (;;) {
1.18 + synchronized (this) {
1.19 + if (cache == null) {
1.20 + cache = new HashMap<From, To>();
1.21 + }
1.22 + To t = cache.get(f);
1.23 + if (t != null) {
1.24 + return t;
1.25 + }
1.26 + }
1.27 +
1.28 + To newT = createItem(f);
1.29 +
1.30 + synchronized (this) {
1.31 + To t = cache.get(f);
1.32 + if (t == null) {
1.33 + cache.put(f, newT);
1.34 + return newT;
1.35 + } else {
1.36 + return t;
1.37 + }
1.38 + }
1.39 + }
1.40 + }
1.41 +
1.42 + protected abstract To createItem(From f);
1.43 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java Tue Feb 10 18:36:21 2009 +0100
2.3 @@ -0,0 +1,131 @@
2.4 +package org.apidesign.javamonitorflaws;
2.5 +
2.6 +import java.beans.PropertyChangeEvent;
2.7 +import java.beans.PropertyChangeListener;
2.8 +import org.netbeans.junit.NbTestCase;
2.9 +
2.10 +public class CacheTest extends NbTestCase {
2.11 + private MultiplyCache cache = new MultiplyCache();
2.12 +
2.13 + public CacheTest(String n) {
2.14 + super(n);
2.15 + }
2.16 +
2.17 + @Override
2.18 + protected int timeOut() {
2.19 + return 2000;
2.20 + }
2.21 +
2.22 + /**
2.23 + * To simulate deadlock between the cache's setMultiply method and
2.24 + * ToDeadlock's own lock. The root cause is that setMultiply is calling
2.25 + * foreign code while holding the caches's <code>this</code> lock:
2.26 +
2.27 +Thread Test Watch Dog: testDeadlockWithSetter
2.28 + org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:19
2.29 + org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40
2.30 + org.apidesign.javamonitorflaws.CacheTest.testDeadlockWithSetter:50
2.31 + sun.reflect.NativeMethodAccessorImpl.invoke0:-2
2.32 + sun.reflect.NativeMethodAccessorImpl.invoke:39
2.33 + sun.reflect.DelegatingMethodAccessorImpl.invoke:25
2.34 + java.lang.reflect.Method.invoke:597
2.35 + junit.framework.TestCase.runTest:168
2.36 + org.netbeans.junit.NbTestCase.access$200:84
2.37 + org.netbeans.junit.NbTestCase$2.doSomething:328
2.38 + org.netbeans.junit.NbTestCase$1Guard.run:265
2.39 + java.lang.Thread.run:619
2.40 +Thread to deadlock
2.41 + org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40
2.42 + org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.propertyChange:36
2.43 + java.beans.PropertyChangeSupport.firePropertyChange:339
2.44 + java.beans.PropertyChangeSupport.firePropertyChange:276
2.45 + java.beans.PropertyChangeSupport.firePropertyChange:297
2.46 + org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21
2.47 + org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.run:28
2.48 + java.lang.Thread.run:619
2.49 +
2.50 + */
2.51 + public void testDeadlockWithSetter() throws Exception {
2.52 + if (Boolean.getBoolean("no.failures")) return;
2.53 +
2.54 + class ToDeadlock implements Runnable, PropertyChangeListener {
2.55 + int value;
2.56 +
2.57 + public void run() {
2.58 + cache.setMultiply(10);
2.59 + }
2.60 + public void propertyChange(PropertyChangeEvent evt) {
2.61 + try {
2.62 + Thread.sleep(500);
2.63 + } catch (InterruptedException ex) {
2.64 + // ok
2.65 + }
2.66 + changeMultiplyToSeven();
2.67 + }
2.68 +
2.69 + public synchronized void changeMultiplyToSeven() {
2.70 + cache.setMultiply(7);
2.71 + }
2.72 + }
2.73 + ToDeadlock toDeadlock = new ToDeadlock();
2.74 + cache.addPropertyChangeListener(toDeadlock);
2.75 + Thread t = new Thread(toDeadlock, "to deadlock");
2.76 + t.start();
2.77 +
2.78 + Thread.sleep(100);
2.79 +
2.80 + toDeadlock.changeMultiplyToSeven();
2.81 + }
2.82 +
2.83 + /** Shows that one can deadlock with the cache's API even the API
2.84 + * is locally correctly synchronized.
2.85 +Thread Test Watch Dog: testDeadlockWithTheAPICacheItself
2.86 + org.apidesign.javamonitorflaws.Cache.get:16
2.87 + org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen
2.88 + org.apidesign.javamonitorflaws.CacheTest.testDeadlockViaAPI:112
2.89 + java.lang.reflect.Method.invoke:597
2.90 + org.netbeans.junit.NbTestCase.access$200:84
2.91 + org.netbeans.junit.NbTestCase$2.doSomething:328
2.92 + org.netbeans.junit.NbTestCase$1Guard.run:265
2.93 + java.lang.Thread.run:619
2.94 +Thread Deadlock using API
2.95 + org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen
2.96 + org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.propertyChange:98
2.97 + java.beans.PropertyChangeSupport.firePropertyChange:339
2.98 + java.beans.PropertyChangeSupport.firePropertyChange:276
2.99 + java.beans.PropertyChangeSupport.firePropertyChange:297
2.100 + org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21
2.101 + org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.run:90
2.102 + java.lang.Thread.run:619
2.103 + */
2.104 + public void testDeadlockViaAPI() throws Exception {
2.105 + if (Boolean.getBoolean("no.failures")) return;
2.106 +
2.107 + class ToDeadlock implements Runnable, PropertyChangeListener {
2.108 + public void run() {
2.109 + cache.setMultiply(10);
2.110 + }
2.111 + public void propertyChange(PropertyChangeEvent evt) {
2.112 + try {
2.113 + Thread.sleep(500);
2.114 + } catch (InterruptedException ex) {
2.115 + // ok
2.116 + }
2.117 + assertMultiplyByTen();
2.118 + }
2.119 +
2.120 + public synchronized void assertMultiplyByTen() {
2.121 + int value = cache.get("123");
2.122 + assertEquals("3*10=30", 30, value);
2.123 + }
2.124 + }
2.125 + ToDeadlock toDeadlock = new ToDeadlock();
2.126 + cache.addPropertyChangeListener(toDeadlock);
2.127 + Thread t = new Thread(toDeadlock, "Deadlock using API");
2.128 + t.start();
2.129 +
2.130 + Thread.sleep(100);
2.131 +
2.132 + toDeadlock.assertMultiplyByTen();
2.133 + }
2.134 +}
2.135 \ No newline at end of file
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java Tue Feb 10 18:36:21 2009 +0100
3.3 @@ -0,0 +1,43 @@
3.4 +package org.apidesign.javamonitorflaws;
3.5 +
3.6 +import org.apidesign.javamonitorflaws.Cache;
3.7 +import java.beans.PropertyChangeListener;
3.8 +import java.beans.PropertyChangeSupport;
3.9 +
3.10 +/**
3.11 + *
3.12 + * @author Jaroslav Tulach <jtulach@netbeans.org>
3.13 + */
3.14 +public class MultiplyCache extends Cache<String,Integer> {
3.15 + private PropertyChangeSupport pcs;
3.16 + private int multiply;
3.17 + public static final String PROP_MULTIPLY = "multiply";
3.18 +
3.19 + public synchronized int getMultiply() {
3.20 + return multiply;
3.21 + }
3.22 + public synchronized void setMultiply(int multiply) {
3.23 + int oldMultiply = this.multiply;
3.24 + this.multiply = multiply;
3.25 + pcs.firePropertyChange(PROP_MULTIPLY, oldMultiply, multiply);
3.26 + }
3.27 +
3.28 + public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
3.29 + if (pcs == null) {
3.30 + pcs = new PropertyChangeSupport(this);
3.31 + }
3.32 + pcs.addPropertyChangeListener(listener);
3.33 + }
3.34 + public void removePropertyChangeListener(PropertyChangeListener listener) {
3.35 + if (pcs != null) {
3.36 + pcs.removePropertyChangeListener(listener);
3.37 + }
3.38 + }
3.39 +
3.40 + @Override
3.41 + protected Integer createItem(String f) {
3.42 + return f.length() * getMultiply();
3.43 + }
3.44 +
3.45 +
3.46 +}
3.47 \ No newline at end of file