Skip to content

Commit

Permalink
Handle UI update dispatch via a separate thread
Browse files Browse the repository at this point in the history
This avoids queuing too many updates for the event dispatch thread via
invokeLater(), but also avoids slowing down processing by directly using
invokeAndWait() in the worker.

The benefits w.r.t. invokeAndWait() seem small and inconsistent, to be
honest, but in one case the improvement was of ~10 cycles/sec (on a
speed of 50 cycles/sec). The code is not too complex so I am going for
this approach.
  • Loading branch information
lupino3 committed May 15, 2017
1 parent c591f4b commit 2f49b2e
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 21 deletions.
39 changes: 18 additions & 21 deletions src/main/java/org/edumips64/ui/swing/CPUSwingWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,11 @@

import static java.lang.Thread.sleep;

/** This class handles the multi-threaded CPU object, and acts as a proxy between
* the Main class and the CPU class.
* @author Antonella Scandura
* @author Andrea Spadaccini
/**
* Swing worker that deals with executing the program and updating the UI.
* */
public class CPUSwingWorker extends SwingWorker<Void, Void> {

/**
* Needed for multithreading.
*/
private int nStep;

/**
Expand Down Expand Up @@ -71,6 +66,7 @@ public class CPUSwingWorker extends SwingWorker<Void, Void> {
private GUIFrontend front;
private JFrame f;
private ConfigStore config;
private GUIUpdateThread guiUpdateThread;

private static final Logger logger = Logger.getLogger(CPUSwingWorker.class.getName());

Expand All @@ -81,6 +77,8 @@ public CPUSwingWorker(CPU cpu, GUIFrontend front, JFrame mainFrame, ConfigStore
f = mainFrame;
this.config = config;
updateConfigValues();
guiUpdateThread = new GUIUpdateThread(front);
guiUpdateThread.start();
}

/**
Expand Down Expand Up @@ -123,6 +121,7 @@ private synchronized void haltCPU() {

@Override
protected Void doInBackground() throws Exception {
long startTimeMs = System.currentTimeMillis();
logger.info("running");

// Let's disable the running menu items and enable the stop menu
Expand Down Expand Up @@ -197,23 +196,18 @@ protected Void doInBackground() throws Exception {
haltCPU();
break;
} finally {
final int currentSteps = steps;
SwingUtilities.invokeLater(() -> {
front.updateComponents();
if (verbose) {
// Very crude rate limiting, works well for very big programs, not so well for short ones.
// TODO: create another worker thread that does updates as quickly as the main Swing thread can do,
// but no faster. For big programs, the CPU finishes pretty quickly, but thousands of invocations are
// scheduled for the main Swing thread, that keeps executing GUI updates after termination.
if (nStep > 0 || (nStep < 0 && currentSteps % 1000 == 0)) {
front.represent();
}
}
});
front.updateComponents();
if (verbose) {
// SwingUtilities.invokeAndWait(() -> front.represent());
guiUpdateThread.triggerUpdate();
}
}
}

SwingUtilities.invokeLater(() -> {
guiUpdateThread.terminate();
guiUpdateThread.join();

SwingUtilities.invokeAndWait(() -> {
// Represent changes, in case the user chose non-verbose mode.
front.represent();

Expand All @@ -224,6 +218,9 @@ protected Void doInBackground() throws Exception {
Main.setStopStatus(false);
Main.stopPB();
});
long endTimeMs = System.currentTimeMillis();
float cyclesPerSecond = steps / ((endTimeMs - startTimeMs) / 1000);
logger.info("Executed " + steps + " steps in " + (endTimeMs - startTimeMs) + " ms. Speed: " + cyclesPerSecond + " cycles/sec");
return null;
}
}
79 changes: 79 additions & 0 deletions src/main/java/org/edumips64/ui/swing/GUIUpdateThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* GUIUpdateThread.java
*
* Thread that updates the UI according to a specified frequency. Used to
* rate-limit UI update requests from the CPUSwingWorker.
*
* (c) 2017 Andrea Spadaccini
*
* This file is part of the EduMIPS64 project, and is released under the GNU
* General Public License.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.edumips64.ui.swing;

import javax.swing.*;
import java.lang.reflect.InvocationTargetException;

public class GUIUpdateThread extends Thread {
private boolean shouldUpdate = false;
private boolean shouldTerminate = false;
private GUIFrontend frontend;
private long periodMs;

GUIUpdateThread(GUIFrontend frontend) {
this.frontend = frontend;
// Rate-limiting to 100 updates / second.
periodMs = 10;
}

// Signals to this thread that an update is necessary. The logic that handles this boolean
// is not synchronized because it is not important to get it perfectly correct.
void triggerUpdate() {
shouldUpdate = true;
}

// Signals to this thread that it should stop executing as quickly as possible.
void terminate() {
shouldTerminate = true;
}

@Override
public void run() {
while(true) {
if (shouldTerminate) {
break;
}
long start = System.currentTimeMillis();
if (shouldUpdate) {
try {
SwingUtilities.invokeAndWait(() -> {
frontend.updateComponents();
frontend.represent();
});
shouldUpdate = false;
} catch (InterruptedException | InvocationTargetException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
try {
Thread.sleep(end - start);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

0 comments on commit 2f49b2e

Please sign in to comment.