samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 30 Oct 2014 21:30:10 +0100
changeset 409 40cabcdcd2be
parent 317 e101649dbd17
permissions -rw-r--r--
Updating to NBMs from NetBeans 8.0.1 as some of them are required to run on JDK8
     1 package org.apidesign.javamonitorflaws;
     2 
     3 import java.beans.PropertyChangeEvent;
     4 import java.beans.PropertyChangeListener;
     5 import org.netbeans.junit.NbTestCase;
     6 
     7 public class CacheTest extends NbTestCase {
     8     public CacheTest(String n) {
     9         super(n);
    10     }
    11 
    12     @Override
    13     protected int timeOut() {
    14         return 2000;
    15     }
    16 
    17     /**
    18      * To simulate deadlock between the cache's setMultiply method and
    19      * ToDeadlock's own lock. The root cause is that setMultiply is calling
    20      * foreign code while holding the caches's <code>this</code> lock:
    21      
    22 Thread Test Watch Dog: testDeadlockWithSetter
    23   org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:19
    24   org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40
    25   org.apidesign.javamonitorflaws.CacheTest.testDeadlockWithSetter:50
    26   sun.reflect.NativeMethodAccessorImpl.invoke0:-2
    27   sun.reflect.NativeMethodAccessorImpl.invoke:39
    28   sun.reflect.DelegatingMethodAccessorImpl.invoke:25
    29   java.lang.reflect.Method.invoke:597
    30   junit.framework.TestCase.runTest:168
    31   org.netbeans.junit.NbTestCase.access$200:84
    32   org.netbeans.junit.NbTestCase$2.doSomething:328
    33   org.netbeans.junit.NbTestCase$1Guard.run:265
    34   java.lang.Thread.run:619
    35 Thread to deadlock
    36   org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40
    37   org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.propertyChange:36
    38   java.beans.PropertyChangeSupport.firePropertyChange:339
    39   java.beans.PropertyChangeSupport.firePropertyChange:276
    40   java.beans.PropertyChangeSupport.firePropertyChange:297
    41   org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21
    42   org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.run:28
    43   java.lang.Thread.run:619
    44      
    45      */
    46     public void testDeadlockWithSetter() throws Exception {
    47         if (Boolean.getBoolean("no.failures")) return;
    48         
    49         final CacheToTest cache = new MultiplyCache();
    50 
    51         class ToDeadlock implements Runnable, PropertyChangeListener {
    52             int value;
    53 
    54             public void run() {
    55                 cache.setMultiply(10);
    56             }
    57             public void propertyChange(PropertyChangeEvent evt) {
    58                 try {
    59                     Thread.sleep(500);
    60                 } catch (InterruptedException ex) {
    61                     // ok
    62                 }
    63                 changeMultiplyToSeven();
    64             }
    65 
    66             public synchronized void changeMultiplyToSeven() {
    67                 cache.setMultiply(7);
    68             }
    69         }
    70         ToDeadlock toDeadlock = new ToDeadlock();
    71         cache.addPropertyChangeListener(toDeadlock);
    72         Thread t = new Thread(toDeadlock, "to deadlock");
    73         t.start();
    74 
    75         Thread.sleep(100);
    76 
    77         toDeadlock.changeMultiplyToSeven();
    78     }
    79 
    80     /** Shows that one can deadlock with the cache's API even the API
    81      * is locally correctly synchronized.
    82 Thread Test Watch Dog: testDeadlockWithTheAPICacheItself
    83   org.apidesign.javamonitorflaws.Cache.get:16
    84   org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen
    85   org.apidesign.javamonitorflaws.CacheTest.testDeadlockViaAPI:112
    86   java.lang.reflect.Method.invoke:597
    87   org.netbeans.junit.NbTestCase.access$200:84
    88   org.netbeans.junit.NbTestCase$2.doSomething:328
    89   org.netbeans.junit.NbTestCase$1Guard.run:265
    90   java.lang.Thread.run:619
    91 Thread Deadlock using API
    92   org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen
    93   org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.propertyChange:98
    94   java.beans.PropertyChangeSupport.firePropertyChange:339
    95   java.beans.PropertyChangeSupport.firePropertyChange:276
    96   java.beans.PropertyChangeSupport.firePropertyChange:297
    97   org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21
    98   org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.run:90
    99   java.lang.Thread.run:619
   100      */
   101     public void testDeadlockViaAPI() throws Exception {
   102         if (Boolean.getBoolean("no.failures")) return;
   103         testDeadlockViaAPI(new MultiplyCache());
   104     }
   105 
   106     // BEGIN: monitor.pitfalls.block.propertychange
   107     private static void testDeadlockViaAPI(final CacheToTest cache)
   108     throws Exception {
   109         class ToDeadlock implements Runnable, PropertyChangeListener {
   110             int lastMultiply;
   111 
   112             public void run() {
   113                 cache.setMultiply(10);
   114             }
   115             public void propertyChange(PropertyChangeEvent evt) {
   116                 try {
   117                     storeMultiply();
   118                 } catch (InterruptedException ex) {
   119                     // ok
   120                 }
   121             }
   122 
   123             private synchronized void storeMultiply()
   124             throws InterruptedException {
   125                 lastMultiply = cache.getMultiply();
   126                 // simulates "starvation"
   127                 wait();
   128             }
   129 
   130             public void assertMultiplyByTen() {
   131             }
   132         }
   133         ToDeadlock toDeadlock = new ToDeadlock();
   134         cache.addPropertyChangeListener(toDeadlock);
   135         Thread t = new Thread(toDeadlock, "Deadlock using API");
   136         t.start();
   137 
   138         Thread.sleep(100);
   139 
   140         // BEGIN: monitor.pitfalls.brokencall
   141         int value =  cache.get("123");
   142         assertEquals("3*10=30", 30, value);
   143         // END: monitor.pitfalls.brokencall
   144     }
   145     // END: monitor.pitfalls.block.propertychange
   146 
   147     public void testDeadlockViaAPIWithCacheOK() throws Exception {
   148         testDeadlockViaAPI(new MultiplyCacheOK());
   149     }
   150 
   151     static interface CacheToTest {
   152         public Integer get(String key);
   153 
   154         public void setMultiply(int m);
   155         public int getMultiply();
   156         public void addPropertyChangeListener(PropertyChangeListener l);
   157         public void removePropertyChangeListener(PropertyChangeListener l);
   158     }
   159 }