Domanda

I am making an emulator for the Gameboy system, and I am using Swing as my GUI. My implementation uses a SwingWorker to run the CPU emulation in a background thread. Through SwingWorker.doInBackground method I publish an array containing the framebuffer (the image data) at regular intervals (whenever the Gameboy emulates a VBlank screen refresh) which is supposed to happen ~60 times a second. I call on Thread.sleep(...) in this background thread to synchronize the Gameboys time with the system time. In the process method, I pass this data onto a JPanel and invoke its repaint method. My doInBackground method is basically an infinite loop with the emulated Gameboy CPU constantly stepping instructions in the background.

The problem I am facing is that on certain ROMs, my application just freezes and my JPanel component shows no screen updates. I tried using println's on my background running thread outputting the instruction being executed by the emulated CPU, and that is stuck as well with no output being generated after awhile.

With some ROMs, I don't see this issue happening at all.

Here is a sample of my SwingWorker code:

package com.sankar.gbemu.swing;

import com.sankar.gbemu.cpu.CPU;
import com.sankar.gbemu.gpu.LCDController;
import com.sankar.gbemu.gpu.VBlankListener;
import java.util.List;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class EmulationWorker extends SwingWorker<Void,int[][]> {


private CPU cpu;
private LCDController lcd;
private GBPanel panel;
private GBRenderer renderer;

public EmulationWorker(CPU cpu, LCDController lcd, GBPanel panel, GBRenderer renderer) {
    this.cpu = cpu;
    this.lcd = lcd;
    this.panel = panel;
    this.renderer = renderer;
    init();
}

private void init() {
    lcd.registerVBlankListener(new VBlankListener() {

        private long lastRefresh = System.nanoTime();

        private static final long REFRESH_INTERVAL = (long)1e9/60;

        @Override
        public void vBlankOccured(int[][] screenData) {
            publish(screenData);

            long currentTime = System.nanoTime();
            long delta = currentTime - lastRefresh;

            lastRefresh = currentTime;

            if (delta < REFRESH_INTERVAL) {
                try {
                    long sleepInterval = (long)((REFRESH_INTERVAL - delta)/1e6);
                    Thread.sleep(sleepInterval);
                } catch(InterruptedException e) {
                    // Ignore
                }
            }
        }
    });
}

@Override
protected Void doInBackground() throws Exception {
    while(true) cpu.step();
}

@Override
protected void process(List<int[][]> updates) {
    if (!SwingUtilities.isEventDispatchThread()) throw new RuntimeException("Not in event dispatch thread");
    renderer.updateScreenData(updates.get(updates.size() - 1));
    panel.repaint();
}

}

I took a jstack trace, and see that my swing-worker-thread is stuck at Unsafe.park:

2013-10-10 22:17:18
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.0-b56 mixed mode):

"Attach Listener" daemon prio=5 tid=0x00007fae12049000 nid=0xc20f waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"TimerQueue" daemon prio=5 tid=0x00007fae1240b800 nid=0xbf03 waiting on condition [0x000000015a085000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000013ac88b60> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
    at java.util.concurrent.DelayQueue.take(DelayQueue.java:209)
    at javax.swing.TimerQueue.run(TimerQueue.java:171)
    at java.lang.Thread.run(Thread.java:724)

"SwingWorker-pool-1-thread-1" daemon prio=5 tid=0x00007fae11194800 nid=0xb92f waiting on condition [0x0000000159f82000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000013ac2c340> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
    at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:724)

"Java2D Disposer" daemon prio=5 tid=0x00007fae11183000 nid=0xac03 in Object.wait() [0x0000000157915000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000013ad250c0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x000000013ad250c0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at sun.java2d.Disposer.run(Disposer.java:145)
    at java.lang.Thread.run(Thread.java:724)

"Java2D Queue Flusher" daemon prio=5 tid=0x00007fae11106800 nid=0x9f0b in Object.wait() [0x0000000156eb0000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000013ac57f18> (a sun.java2d.opengl.OGLRenderQueue$QueueFlusher)
    at sun.java2d.opengl.OGLRenderQueue$QueueFlusher.run(OGLRenderQueue.java:208)
    - locked <0x000000013ac57f18> (a sun.java2d.opengl.OGLRenderQueue$QueueFlusher)

"DestroyJavaVM" prio=5 tid=0x00007fae12003000 nid=0x1007 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"AWT-EventQueue-0" prio=5 tid=0x00007fae110ec800 nid=0x9d03 waiting on condition [0x0000000156dad000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000013ac8b3f0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
    at java.awt.EventQueue.getNextEvent(EventQueue.java:543)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

"AWT-Shutdown" prio=5 tid=0x00007fae110d5800 nid=0x6e03 in Object.wait() [0x0000000154562000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000013ad26900> (a java.lang.Object)
    at java.lang.Object.wait(Object.java:503)
    at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:287)
    - locked <0x000000013ad26900> (a java.lang.Object)
    at java.lang.Thread.run(Thread.java:724)

"AppKit Thread" daemon prio=5 tid=0x00007fae12120000 nid=0x2617 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Service Thread" daemon prio=5 tid=0x00007fae12081000 nid=0x5703 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=5 tid=0x00007fae12078800 nid=0x5503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=5 tid=0x00007fae1207f000 nid=0x5303 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=5 tid=0x00007fae12077800 nid=0x5103 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=5 tid=0x00007fae12043000 nid=0x3e03 in Object.wait() [0x000000015283c000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000013ac08d80> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x000000013ac08d80> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)

"Reference Handler" daemon prio=5 tid=0x00007fae12040800 nid=0x3c03 in Object.wait() [0x0000000152739000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000013ac08ad0> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:503)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
    - locked <0x000000013ac08ad0> (a java.lang.ref.Reference$Lock)

"VM Thread" prio=5 tid=0x00007fae12040000 nid=0x3a03 runnable 

"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fae1200f800 nid=0x3603 runnable 

"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fae12010000 nid=0x3803 runnable 

"VM Periodic Task Thread" prio=5 tid=0x00007fae12089800 nid=0x5903 waiting on condition 

JNI global references: 344

I'm not sure what could possibly be wrong since all my Swing component updates are being done only on the EventDispatch thread.

I am running Java(TM) SE Runtime Environment (build 1.7.0_40-b43) for Mac OS X Mountain Lion which is the latest version that was available.

È stato utile?

Soluzione

The doInBackground method was actually throwing an exception. Since I did not implement the done method and obtain the result using get, the exception was swallowed and went unnoticed.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top