Эмуляция Gameboy - SwingWorker застрял в Unsafe.park
Я делаю эмулятор для системы Gameboy, и я использую Swing в качестве моего GUI. Моя реализация использует SwingWorker
запустить эмуляцию процессора в фоновом потоке. Через SwingWorker.doInBackground
Метод Я публикую массив, содержащий кадровый буфер (данные изображения) через регулярные промежутки времени (всякий раз, когда Gameboy эмулирует обновление экрана VBlank), что должно происходить ~60 раз в секунду. Я призываю Thread.sleep(...)
в этом фоновом потоке синхронизировать время Gameboy s с системным временем. в process
метод, я передаю эти данные на JPanel
и вызвать его repaint
метод. мой doInBackground
Метод в основном представляет собой бесконечный цикл с эмулируемым процессором Gameboy, постоянно выполняющим инструкции в фоне.
Проблема, с которой я сталкиваюсь, заключается в том, что на некоторых ПЗУ мое приложение просто зависает, а мое JPanel
Компонент не показывает обновления экрана. Я пытался с помощью println
В моем фоновом режиме запущен поток, выводящий инструкцию, выполняемую эмулированным ЦП, и это застревает, и через некоторое время вывод не генерируется.
С некоторыми ПЗУ я не вижу, чтобы эта проблема происходила вообще.
Вот пример моего кода 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 и увидел, что мой рабочий поток колеблется в 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.
Я использую Java(TM) SE Runtime Environment (сборка 1.7.0_40-b43) для Mac OS X Mountain Lion, которая является последней доступной версией.
1 ответ
doInBackground
Метод на самом деле выбрасывал исключение. Так как я не реализовал done
метод и получить результат с помощью get
Исключение было проглочено и осталось незамеченным.