/* CPUSwingWorker.java
*
* Swing worker that deals with executing the program and updating the UI.
*
* (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 org.edumips64.*;
import org.edumips64.core.*;
import org.edumips64.core.is.*;
import org.edumips64.utils.*;
import java.util.logging.Logger;
import javax.swing.*;
import static java.lang.Thread.sleep;
/**
* Swing worker that deals with executing the program and updating the UI.
* */
public class CPUSwingWorker extends SwingWorker<Void, Void> {
private int nStep;
/**
* Booleans for synchronous exception handling
*/
private boolean masked;
private boolean terminateOnSynchronousExceptions;
/**
* Boolean describing if the CPU is running
*/
private boolean externalStop;
/**
* Verbose mode
*/
private boolean verbose;
/**
* Sleep interval between cycles in verbose mode
*/
private int sleep_interval;
private CPU cpu;
private GUIFrontend front;
private JFrame f;
private ConfigStore config;
private GUIUpdateThread guiUpdateThread;
private static final Logger logger = Logger.getLogger(CPUSwingWorker.class.getName());
public CPUSwingWorker(CPU cpu, GUIFrontend front, JFrame mainFrame, ConfigStore config) {
externalStop = false;
this.cpu = cpu;
this.front = front;
f = mainFrame;
this.config = config;
updateConfigValues();
guiUpdateThread = new GUIUpdateThread(front);
guiUpdateThread.start();
}
/**
* Used to refresh the internal configuration values. Takes the needed
* configuration values from the configuration file.
*/
public void updateConfigValues() {
sleep_interval = config.getInt(ConfigKey.SLEEP_INTERVAL);
verbose = config.getBoolean(ConfigKey.VERBOSE);
masked = config.getBoolean(ConfigKey.SYNC_EXCEPTIONS_MASKED);
terminateOnSynchronousExceptions = config.getBoolean(ConfigKey.SYNC_EXCEPTIONS_TERMINATE);
logger.info("Terminate = " + terminateOnSynchronousExceptions + "; masked = " + masked);
}
/**
* Allows external classes to stop the execution.
*/
public synchronized void stopExecution() {
externalStop = true;
}
/**
* Sets the number of cpu cycles. Set a negative number if you want the CPU
* to cycle endlessly.
*
* @param n an integer value
*/
public synchronized void setSteps(int n) {
nStep = n;
}
private synchronized void haltCPU() {
logger.info("Halting the CPU.");
front.updateComponents();
cpu.setStatus(CPU.CPUStatus.HALTED);
Main.changeShownMenuItems(CPU.CPUStatus.HALTED);
}
@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
// item
Main.setRunningMenuItemsStatus(false);
Main.setStopStatus(true);
// Progress bar
SwingUtilities.invokeLater(Main::startPB);
// If the nStep variable is set to a value < 0, then we must loop forever (an exception
// will be the way to terminate execution); otherwise, we must be looping only
// 'nStep' number of times.
int steps = 0;
while (true) {
if (steps++ == nStep) {
break;
}
if (verbose && (sleep_interval != 0)) {
sleep(sleep_interval);
}
// TODO: implement
if (externalStop) {
logger.info("Stopping cycles because of external interaction.");
break;
}
try {
cpu.step();
} catch (StoppedCPUException ex) {
logger.info("CPUGUIThread: CPU was stopped");
break;
} catch (BreakException ex) {
break;
} catch (SynchronousException ex) {
logger.info("Caught a synchronous exception.");
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(f, CurrentLocale.getString(ex.getCode() + ".Message"), "EduMIPS64 - " + CurrentLocale.getString("EXCEPTION"), JOptionPane.ERROR_MESSAGE);
front.updateComponents();
front.represent();
});
if (terminateOnSynchronousExceptions) {
haltCPU();
break;
}
} catch (HaltException ex) {
haltCPU();
logger.info("CPUGUIThread: CPU Halted because of HaltException.");
break;
} catch (NotAlignException ex) {
logger.info("NotAlignException. " + ex);
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(Main.ioFrame, ex.getMessage(), "EduMIPS64 - " + CurrentLocale.getString("ERROR"), JOptionPane.ERROR_MESSAGE));
haltCPU();
break;
} catch (AddressErrorException ex) {
logger.info("AddressErrorException. " + ex);
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(org.edumips64.Main.ioFrame, ex.getMessage(), "EduMIPS64 - " + CurrentLocale.getString("ERROR"), JOptionPane.ERROR_MESSAGE));
haltCPU();
break;
} catch (MemoryElementNotFoundException ex) {
logger.info("Attempt to read a non-existent cell (MemoryElementNotFoundException). " + ex);
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(org.edumips64.Main.ioFrame, CurrentLocale.getString("ERROR_LABEL"), "EduMIPS64 - " + CurrentLocale.getString("ERROR"), JOptionPane.ERROR_MESSAGE));
haltCPU();
break;
} catch (Exception ex) {
logger.severe("Exception in CPUSwingWorker: " + ex);
SwingUtilities.invokeLater(() -> new ReportDialog(f, ex, CurrentLocale.getString("GUI_STEP_ERROR")));
haltCPU();
break;
} finally {
front.updateComponents();
if (verbose) {
// SwingUtilities.invokeAndWait(() -> front.represent());
guiUpdateThread.triggerUpdate();
}
}
}
guiUpdateThread.terminate();
guiUpdateThread.join();
SwingUtilities.invokeAndWait(() -> {
// Represent changes, in case the user chose non-verbose mode.
front.represent();
if (cpu.getStatus() != CPU.CPUStatus.HALTED) {
Main.setRunningMenuItemsStatus(true);
}
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;
}
}