# HG changeset patch # User Jaroslav Tulach # Date 1234432502 -3600 # Node ID 6c1d8b5553d89c0c520085efb31dbd0accc01291 # Parent 38ebce3000fdf43817059674c1708c4a3a3defbe Changing the test to run with both version, the bad Cache as well as CacheOK. Making sure the same test passes for CacheOK diff -r 38ebce3000fd -r 6c1d8b5553d8 samples/deadlock/src/org/apidesign/javamonitorflaws/CacheOK.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/deadlock/src/org/apidesign/javamonitorflaws/CacheOK.java Thu Feb 12 10:55:02 2009 +0100 @@ -0,0 +1,54 @@ +package org.apidesign.javamonitorflaws; + +import java.util.HashMap; +import java.util.Map; + +/** Classical caching support class that makes sure there is + * always one "To" value for each "From" one returned from the {@link #get} + * method. However it does not prevent multiple threads to call + * {@link #createItem} multiple times for the same "From" value. + *

+ * In contrast to {@link Cache}, this is correctly synchronized. + * + * @author Jaroslav Tulach + */ +// BEGIN: monitor.pitfalls.CacheOK +public abstract class CacheOK { + private Object LOCK = new Object(); + + private Map cache; + + protected abstract To createItem(From f); + + public final To get(From f) { + To t = inspectValue(f); + if (t != null) { + return t; + } + To newT = createItem(f); + return registerValue(f, newT); + } + + + private To inspectValue(From f) { + synchronized (LOCK) { + if (cache == null) { + cache = new HashMap(); + } + return cache.get(f); + } + } + + private To registerValue(From f, To newT) { + synchronized (LOCK) { + To t = cache.get(f); + if (t == null) { + cache.put(f, newT); + return newT; + } else { + return t; + } + } + } +} +// END: monitor.pitfalls.CacheOK diff -r 38ebce3000fd -r 6c1d8b5553d8 samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java --- a/samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java Wed Feb 11 10:16:43 2009 +0100 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java Thu Feb 12 10:55:02 2009 +0100 @@ -5,8 +5,6 @@ import org.netbeans.junit.NbTestCase; public class CacheTest extends NbTestCase { - private MultiplyCache cache = new MultiplyCache(); - public CacheTest(String n) { super(n); } @@ -48,6 +46,8 @@ public void testDeadlockWithSetter() throws Exception { if (Boolean.getBoolean("no.failures")) return; + final CacheToTest cache = new MultiplyCache(); + class ToDeadlock implements Runnable, PropertyChangeListener { int value; @@ -98,10 +98,14 @@ org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.run:90 java.lang.Thread.run:619 */ - // BEGIN: monitor.pitfalls.block.propertychange public void testDeadlockViaAPI() throws Exception { if (Boolean.getBoolean("no.failures")) return; - + testDeadlockViaAPI(new MultiplyCache()); + } + + // BEGIN: monitor.pitfalls.block.propertychange + private static void testDeadlockViaAPI(final CacheToTest cache) + throws Exception { class ToDeadlock implements Runnable, PropertyChangeListener { int lastMultiply; @@ -139,4 +143,17 @@ // END: monitor.pitfalls.brokencall } // END: monitor.pitfalls.block.propertychange + + public void testDeadlockViaAPIWithCacheOK() throws Exception { + testDeadlockViaAPI(new MultiplyCacheOK()); + } + + static interface CacheToTest { + public Integer get(String key); + + public void setMultiply(int m); + public int getMultiply(); + public void addPropertyChangeListener(PropertyChangeListener l); + public void removePropertyChangeListener(PropertyChangeListener l); + } } \ No newline at end of file diff -r 38ebce3000fd -r 6c1d8b5553d8 samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java --- a/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java Wed Feb 11 10:16:43 2009 +0100 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java Thu Feb 12 10:55:02 2009 +0100 @@ -1,6 +1,5 @@ package org.apidesign.javamonitorflaws; -import org.apidesign.javamonitorflaws.Cache; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; @@ -9,7 +8,8 @@ * @author Jaroslav Tulach */ // BEGIN: monitor.pitfalls.subclass -public class MultiplyCache extends Cache { +public class MultiplyCache extends Cache +implements CacheTest.CacheToTest { private PropertyChangeSupport pcs; private int multiply; public static final String PROP_MULTIPLY = "multiply"; @@ -41,7 +41,7 @@ @Override protected Integer createItem(String f) { - return f.length() * getMultiply(); + return f.length() * multiply; } } // END: monitor.pitfalls.subclass diff -r 38ebce3000fd -r 6c1d8b5553d8 samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCacheOK.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCacheOK.java Thu Feb 12 10:55:02 2009 +0100 @@ -0,0 +1,49 @@ +package org.apidesign.javamonitorflaws; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +/** + * + * @author Jaroslav Tulach + */ +// BEGIN: monitor.pitfalls.subclassok +public class MultiplyCacheOK extends CacheOK +implements CacheTest.CacheToTest { + private PropertyChangeSupport pcs; + private int multiply; + public static final String PROP_MULTIPLY = "multiply"; + + public synchronized int getMultiply() { + return multiply; + } + public synchronized void setMultiply(int multiply) { + int oldMultiply = this.multiply; + this.multiply = multiply; + pcs.firePropertyChange(PROP_MULTIPLY, oldMultiply, multiply); + } + + public synchronized void addPropertyChangeListener( + PropertyChangeListener listener + ) { + if (pcs == null) { + pcs = new PropertyChangeSupport(this); + } + pcs.addPropertyChangeListener(listener); + } + public synchronized void removePropertyChangeListener( + PropertyChangeListener listener + ) { + if (pcs != null) { + pcs.removePropertyChangeListener(listener); + } + } + + @Override + protected Integer createItem(String f) { + return f.length() * multiply; + } +} +// END: monitor.pitfalls.subclassok + +