Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Strange JRC Race Condition

By: dig090 on Jun 13th, 2012  |  syntax: Java  |  size: 3.07 KB  |  views: 239  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. package com.mprew.be.service.x;
  2.  
  3. import static org.junit.Assert.assertEquals;
  4.  
  5. import java.util.ArrayList;
  6. import java.util.HashMap;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Timer;
  10. import java.util.TimerTask;
  11.  
  12. import org.junit.Test;
  13.  
  14. /**
  15.  * This is an interesting test case that seems to be indicating a JRE issue.
  16.  *
  17.  * This comes from http://stackoverflow.com/q/10982941/179850 and the poster named "Luke".
  18.  *
  19.  * I've boiled the code down to its lowest level and added some comments around the changes that can change the error
  20.  * count. This is pretty repeatable to me with 4/5 runs generating errors. If you can't get it to fail I'd try
  21.  * decreasing the fixed-rate period and increasing the NUMBER_TO_USE constant.
  22.  */
  23. public class StrangeRaceConditionTest {
  24.  
  25.         // if this is decreased it may remove the errors
  26.         public static final int NUMBER_TO_USE = 1000000;
  27.  
  28.         private Map<Integer, Buffer> bufferMap = new HashMap<Integer, Buffer>();
  29.  
  30.         public static void main(String[] args) {
  31.                 new StrangeRaceConditionTest().strangeRaceConditionTest();
  32.         }
  33.  
  34.         @Test
  35.         public void strangeRaceConditionTest() {
  36.                 final Buffer buffer = getBuffer();
  37.  
  38.                 TimerTask getBufferTask = new TimerTask() {
  39.                         @Override
  40.                         public void run() {
  41.                                 buffer.getBuffer();
  42.                         }
  43.                 };
  44.                 // if the period is increased it seems to reduce the errors
  45.                 new Timer(true).scheduleAtFixedRate(getBufferTask, 0 /* delay ms */, 10 /* period ms */);
  46.  
  47.                 for (long i = 0; i < NUMBER_TO_USE; i++) {
  48.                         // if we inline the remove() method here then no errors
  49.                         // if we change this to buffer.remove() then no errors
  50.                         remove();
  51.                 }
  52.  
  53.                 assertEquals(0, buffer.getErrorC());
  54.         }
  55.  
  56.         private synchronized void remove() {
  57.                 Buffer buffer = getBuffer();
  58.                 buffer.remove();
  59.         }
  60.  
  61.         private synchronized Buffer getBuffer() {
  62.                 // if we remove this whole map nonesense then no errors
  63.                 Buffer buffer = bufferMap.get(1);
  64.                 // if this test/else is flipped so it is buffer == null ... then no errors
  65.                 if (buffer != null) {
  66.                         // if this line is commented out then no errors
  67.                         buffer = bufferMap.get(1);
  68.                 } else {
  69.                         buffer = new Buffer();
  70.                         bufferMap.put(1, buffer);
  71.                 }
  72.                 return buffer;
  73.         }
  74.  
  75.         // moving this out to its own class file doesn't seem to help
  76.         private static class Buffer {
  77.                 private List<Integer> list = new ArrayList<Integer>();
  78.                 private boolean insideGetBuffer = false;
  79.                 private int errorC = 0;
  80.  
  81.                 public Buffer() {
  82.                         // initialize the list
  83.                         for (long i = 0; i < NUMBER_TO_USE; i++) {
  84.                                 list.add(null);
  85.                         }
  86.                 }
  87.  
  88.                 public synchronized void remove() {
  89.                         if (insideGetBuffer) {
  90.                                 // adding a System.out.println here makes the problem more repeatable
  91.                                 // System.out.println("How did we get here?");
  92.                                 errorC++;
  93.                         }
  94.                 }
  95.  
  96.                 public synchronized void getBuffer() {
  97.                         insideGetBuffer = true;
  98.                         try {
  99.                                 // if you comment out this sleep then no errors
  100.                                 Thread.sleep(5);
  101.                         } catch (Exception e) {
  102.                                 e.printStackTrace();
  103.                         } finally {
  104.                                 insideGetBuffer = false;
  105.                         }
  106.                 }
  107.  
  108.                 public synchronized int getErrorC() {
  109.                         return errorC;
  110.                 }
  111.         }
  112. }