【问题标题】:Gameboy emulation - SwingWorker stuck at Unsafe.parkGameboy 仿真 - SwingWorker 卡在 Unsafe.park
【发布时间】:2013-10-11 02:46:44
【问题描述】:

我正在为 Gameboy 系统制作模拟器,并使用 Swing 作为我的 GUI。我的实现使用SwingWorker 在后台线程中运行 CPU 仿真。通过SwingWorker.doInBackground 方法,我定期发布一个包含帧缓冲区(图像数据)的数组(每当 Gameboy 模拟 VBlank 屏幕刷新时),这应该每秒发生约 60 次。我在这个后台线程中调用Thread.sleep(...) 来同步Gameboys 时间和系统时间。在process 方法中,我将此数据传递给JPanel 并调用它的repaint 方法。我的doInBackground 方法基本上是一个无限循环,模拟的 Gameboy CPU 在后台不断执行指令。

我面临的问题是,在某些 ROM 上,我的应用程序只是冻结,而我的 JPanel 组件没有显示屏幕更新。我尝试在我的后台运行线程上使用println's 输出模拟CPU正在执行的指令,并且这也卡住了一段时间后没有生成任何输出。

对于某些 ROM,我根本看不到这个问题发生。

这是我的 SwingWorker 代码示例:

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();
}

}

我进行了 jstack 跟踪,发现我的 swing-worker-thread 卡在 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

我不确定可能出了什么问题,因为我所有的 Swing 组件更新都只在 EventDispatch 线程上完成。

我正在为 Mac OS X Mountain Lion 运行 Java(TM) SE 运行时环境(内部版本 1.7.0_40-b43),这是可用的最新版本。

【问题讨论】:

  • 你必须用vBlankOccured方法睡觉吗?
  • 移除睡眠并没有帮助 - 我通过睡眠来控制 CPU 的运行速度
  • 这更多的是好奇而不是可能的修复;)

标签: java swing swingworker emulation gameboy


【解决方案1】:

doInBackground 方法实际上是在抛出异常。由于我没有实现done方法并使用get获取结果,所以异常被吞没了,没有注意到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-14
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 2014-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多