jtulach@315: package org.apidesign.javamonitorflaws; jtulach@315: jtulach@315: import java.beans.PropertyChangeEvent; jtulach@315: import java.beans.PropertyChangeListener; jtulach@315: import org.netbeans.junit.NbTestCase; jtulach@315: jtulach@315: public class CacheTest extends NbTestCase { jtulach@315: public CacheTest(String n) { jtulach@315: super(n); jtulach@315: } jtulach@315: jtulach@315: @Override jtulach@315: protected int timeOut() { jtulach@315: return 2000; jtulach@315: } jtulach@315: jtulach@315: /** jtulach@315: * To simulate deadlock between the cache's setMultiply method and jtulach@315: * ToDeadlock's own lock. The root cause is that setMultiply is calling jtulach@315: * foreign code while holding the caches's this lock: jtulach@315: jtulach@315: Thread Test Watch Dog: testDeadlockWithSetter jtulach@315: org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:19 jtulach@315: org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40 jtulach@315: org.apidesign.javamonitorflaws.CacheTest.testDeadlockWithSetter:50 jtulach@315: sun.reflect.NativeMethodAccessorImpl.invoke0:-2 jtulach@315: sun.reflect.NativeMethodAccessorImpl.invoke:39 jtulach@315: sun.reflect.DelegatingMethodAccessorImpl.invoke:25 jtulach@315: java.lang.reflect.Method.invoke:597 jtulach@315: junit.framework.TestCase.runTest:168 jtulach@315: org.netbeans.junit.NbTestCase.access$200:84 jtulach@315: org.netbeans.junit.NbTestCase$2.doSomething:328 jtulach@315: org.netbeans.junit.NbTestCase$1Guard.run:265 jtulach@315: java.lang.Thread.run:619 jtulach@315: Thread to deadlock jtulach@315: org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40 jtulach@315: org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.propertyChange:36 jtulach@315: java.beans.PropertyChangeSupport.firePropertyChange:339 jtulach@315: java.beans.PropertyChangeSupport.firePropertyChange:276 jtulach@315: java.beans.PropertyChangeSupport.firePropertyChange:297 jtulach@315: org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21 jtulach@315: org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.run:28 jtulach@315: java.lang.Thread.run:619 jtulach@315: jtulach@315: */ jtulach@315: public void testDeadlockWithSetter() throws Exception { jtulach@315: if (Boolean.getBoolean("no.failures")) return; jtulach@315: jtulach@319: final CacheToTest cache = new MultiplyCache(); jtulach@319: jtulach@315: class ToDeadlock implements Runnable, PropertyChangeListener { jtulach@315: int value; jtulach@315: jtulach@315: public void run() { jtulach@315: cache.setMultiply(10); jtulach@315: } jtulach@315: public void propertyChange(PropertyChangeEvent evt) { jtulach@315: try { jtulach@315: Thread.sleep(500); jtulach@315: } catch (InterruptedException ex) { jtulach@315: // ok jtulach@315: } jtulach@315: changeMultiplyToSeven(); jtulach@315: } jtulach@315: jtulach@315: public synchronized void changeMultiplyToSeven() { jtulach@315: cache.setMultiply(7); jtulach@315: } jtulach@315: } jtulach@315: ToDeadlock toDeadlock = new ToDeadlock(); jtulach@315: cache.addPropertyChangeListener(toDeadlock); jtulach@315: Thread t = new Thread(toDeadlock, "to deadlock"); jtulach@315: t.start(); jtulach@315: jtulach@315: Thread.sleep(100); jtulach@315: jtulach@315: toDeadlock.changeMultiplyToSeven(); jtulach@315: } jtulach@315: jtulach@315: /** Shows that one can deadlock with the cache's API even the API jtulach@315: * is locally correctly synchronized. jtulach@315: Thread Test Watch Dog: testDeadlockWithTheAPICacheItself jtulach@315: org.apidesign.javamonitorflaws.Cache.get:16 jtulach@315: org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen jtulach@315: org.apidesign.javamonitorflaws.CacheTest.testDeadlockViaAPI:112 jtulach@315: java.lang.reflect.Method.invoke:597 jtulach@315: org.netbeans.junit.NbTestCase.access$200:84 jtulach@315: org.netbeans.junit.NbTestCase$2.doSomething:328 jtulach@315: org.netbeans.junit.NbTestCase$1Guard.run:265 jtulach@315: java.lang.Thread.run:619 jtulach@315: Thread Deadlock using API jtulach@315: org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen jtulach@315: org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.propertyChange:98 jtulach@315: java.beans.PropertyChangeSupport.firePropertyChange:339 jtulach@315: java.beans.PropertyChangeSupport.firePropertyChange:276 jtulach@315: java.beans.PropertyChangeSupport.firePropertyChange:297 jtulach@315: org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21 jtulach@315: org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.run:90 jtulach@315: java.lang.Thread.run:619 jtulach@315: */ jtulach@315: public void testDeadlockViaAPI() throws Exception { jtulach@315: if (Boolean.getBoolean("no.failures")) return; jtulach@319: testDeadlockViaAPI(new MultiplyCache()); jtulach@319: } jtulach@319: jtulach@319: // BEGIN: monitor.pitfalls.block.propertychange jtulach@319: private static void testDeadlockViaAPI(final CacheToTest cache) jtulach@319: throws Exception { jtulach@315: class ToDeadlock implements Runnable, PropertyChangeListener { jtulach@317: int lastMultiply; jtulach@317: jtulach@315: public void run() { jtulach@315: cache.setMultiply(10); jtulach@315: } jtulach@315: public void propertyChange(PropertyChangeEvent evt) { jtulach@315: try { jtulach@317: storeMultiply(); jtulach@315: } catch (InterruptedException ex) { jtulach@315: // ok jtulach@315: } jtulach@315: } jtulach@315: jtulach@317: private synchronized void storeMultiply() jtulach@317: throws InterruptedException { jtulach@317: lastMultiply = cache.getMultiply(); jtulach@317: // simulates "starvation" jtulach@317: wait(); jtulach@317: } jtulach@317: jtulach@317: public void assertMultiplyByTen() { jtulach@315: } jtulach@315: } jtulach@315: ToDeadlock toDeadlock = new ToDeadlock(); jtulach@315: cache.addPropertyChangeListener(toDeadlock); jtulach@315: Thread t = new Thread(toDeadlock, "Deadlock using API"); jtulach@315: t.start(); jtulach@315: jtulach@315: Thread.sleep(100); jtulach@315: jtulach@317: // BEGIN: monitor.pitfalls.brokencall jtulach@317: int value = cache.get("123"); jtulach@317: assertEquals("3*10=30", 30, value); jtulach@317: // END: monitor.pitfalls.brokencall jtulach@315: } jtulach@317: // END: monitor.pitfalls.block.propertychange jtulach@319: jtulach@319: public void testDeadlockViaAPIWithCacheOK() throws Exception { jtulach@319: testDeadlockViaAPI(new MultiplyCacheOK()); jtulach@319: } jtulach@319: jtulach@319: static interface CacheToTest { jtulach@319: public Integer get(String key); jtulach@319: jtulach@319: public void setMultiply(int m); jtulach@319: public int getMultiply(); jtulach@319: public void addPropertyChangeListener(PropertyChangeListener l); jtulach@319: public void removePropertyChangeListener(PropertyChangeListener l); jtulach@319: } jtulach@315: }