[messages] [Developers] Hang on exit from GTS modules

Joel Uckelman uckelman at nomic.net
Sun Feb 17 13:36:08 MST 2013


Thus spake Brent Easton:
> Hi Joel,
> 
> I have a problem with my GTS modules containing my custom Devil's
> Cauldron code. The modules are hanging on exit in ZipArchive.revert() at
> the w.lock() statement. This is being called during the
> DataAtchive.close() call. 
> 
> It only happens if you instantiate one of the counters that includes my
> custom code. I can't see it, any ideas?

The problem isn't in the tdc package, it's in the terrain package. Here's
the stack trace in which there's an input stream which is created, but
not closed:

java.lang.Exception: Stack trace
  at java.lang.Thread.dumpStack(Thread.java:1342)
  at VASSAL.tools.io.ZipArchive$ZipArchiveInputStream.<init>(ZipArchive.java:632)
  at VASSAL.tools.io.ZipArchive.getInputStream(ZipArchive.java:222)
  at VASSAL.tools.DataArchive.getInputStreamImpl(DataArchive.java:247)
  at VASSAL.tools.DataArchive.getInputStream(DataArchive.java:207)
  at VASSAL.tools.DataArchive.getFileStream(DataArchive.java:760)
  at terrain.TerrainMap.load(TerrainMap.java:326)
  at terrain.TerrainMap.<init>(TerrainMap.java:73)
  at terrain.TerrainDefinitions.getTerrainMap(TerrainDefinitions.java:69)
  at terrain.TerrainHexGrid.getTerrainMap(TerrainHexGrid.java:75)
  at terrain.TerrainHexGrid.getTerrainName(TerrainHexGrid.java:63)
  at tdc.TdcMap.getTerrainProperty(TdcMap.java:89)
  at tdc.UnitInfo.findBasicInfo(UnitInfo.java:381)
  at tdc.UnitInfo.<init>(UnitInfo.java:159)
  at tdc.TdcHighlighter.draw(TdcHighlighter.java:65)
  at VASSAL.build.module.map.StackMetrics.draw(StackMetrics.java:355)
  at VASSAL.build.module.Map.drawPiecesInRegion(Map.java:1510)
  at VASSAL.build.module.Map.paintRegion(Map.java:1478)
  at VASSAL.build.module.Map.paintRegion(Map.java:1471)
  at VASSAL.build.module.Map$View.paint(Map.java:2591)
  at javax.swing.JComponent.paintChildren(JComponent.java:878)
  at javax.swing.JComponent.paint(JComponent.java:1054)
  at javax.swing.JViewport.paint(JViewport.java:731)
  at javax.swing.JComponent.paintChildren(JComponent.java:878)
  at javax.swing.JComponent.paint(JComponent.java:1054)
  at javax.swing.JComponent.paintChildren(JComponent.java:878)
  at javax.swing.JComponent.paint(JComponent.java:1054)
  at javax.swing.JLayeredPane.paint(JLayeredPane.java:585)
  at javax.swing.JComponent.paintChildren(JComponent.java:878)
  at javax.swing.JSplitPane.paintChildren(JSplitPane.java:1047)
  at javax.swing.JComponent.paint(JComponent.java:1054)
  at javax.swing.JComponent.paintChildren(JComponent.java:878)
  at javax.swing.JComponent.paint(JComponent.java:1054)
  at javax.swing.JComponent.paintChildren(JComponent.java:878)
  at javax.swing.JComponent.paint(JComponent.java:1054)
  at javax.swing.JLayeredPane.paint(JLayeredPane.java:585)
  at javax.swing.JComponent.paintChildren(JComponent.java:878)
  at javax.swing.JComponent.paint(JComponent.java:1054)
  at javax.swing.JComponent.paintToOffscreen(JComponent.java:5212)
  at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:295)
  at javax.swing.RepaintManager.paint(RepaintManager.java:1236)
  at javax.swing.JComponent._paintImmediately(JComponent.java:5160)
  at javax.swing.JComponent.paintImmediately(JComponent.java:4971)
  at javax.swing.RepaintManager$3.run(RepaintManager.java:796)
  at javax.swing.RepaintManager$3.run(RepaintManager.java:784)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
  at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:784)
  at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:757)
  at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:706)
  at javax.swing.RepaintManager.access$1000(RepaintManager.java:62)
  at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1651)
  at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
  at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:727)
  at java.awt.EventQueue.access$200(EventQueue.java:103)
  at java.awt.EventQueue$3.run(EventQueue.java:688)
  at java.awt.EventQueue$3.run(EventQueue.java:686)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
  at java.awt.EventQueue.dispatchEvent(EventQueue.java:697)
  at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
  at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
  at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
  at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)

(How I found this: I changed ZipArchiveInputStream so that its ctor
prints "this" and a stack trace, and its close() method prints "this".
Then, I looked for the ZAIS which showed up only once in the log. That's
the one which isn't being closed properly.)

The line numbers for TerrainMap seem to be off a bit from what's in
the repo now; the bug is here:

  public void load() {
    try {
      final DataArchive archive = GameModule.getGameModule().getDataArchive();
      final InputStream stream = archive.getFileStream(getMapFileName(board));
      final InputStreamReader reader = new InputStreamReader(stream, CHAR_SET);
      final BufferedReader buffer = new BufferedReader(reader);
      for (String line = buffer.readLine(); line != null; line = buffer.readLine()) {
        if (line.startsWith(TerrainHex.TYPE)) {
          addHexTerrain(line);
        }
        else if (line.startsWith(TerrainEdge.TYPE)) {
          addEdgeTerrain(line);
        }
        else if (line.startsWith(TerrainLine.TYPE)) {
          addLineTerrain(line);
        }
        else if (line.startsWith(TerrainAttribute.TYPE)) {
          addAttributeTerrain(line);
        }
      }
    }
    catch (Exception e) {
      
    }
  }

What you need is to make sure the stream is closed, no matter what
else happens:

  public void load() {
    try {
      final DataArchive archive = GameModule.getGameModule().getDataArchive();
    
      InputStream stream = null;
      try {
        stream = archive.getFileStream(getMapFileName(board));

        final InputStreamReader reader = new InputStreamReader(stream, CHAR_SET);
        final BufferedReader buffer = new BufferedReader(reader);
        for (String line = buffer.readLine(); line != null; line = buffer.readLine()) {
          if (line.startsWith(TerrainHex.TYPE)) {
            addHexTerrain(line);
          }
          else if (line.startsWith(TerrainEdge.TYPE)) {
            addEdgeTerrain(line);
          }
          else if (line.startsWith(TerrainLine.TYPE)) {
            addLineTerrain(line);
          }
          else if (line.startsWith(TerrainAttribute.TYPE)) {
            addAttributeTerrain(line);
          }
        }
      }
      finally {
        IOUtils.closeQuietly(stream);
      }
    }
    catch (Exception e) {

    }
  }

This was the only unclosed stream I saw, so I suspect this will be
sufficient to solve the problem.

-- 
J.


More information about the messages mailing list