From 6d5597b070c7bedce15047c7c560249965cc0bfc Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 25 Jan 2016 18:19:48 +0100 Subject: [PATCH] Avoid multiple concurrent compile/upload operations Disable Compile/Run buttons as they get press, and reenable only on function exit. The launched upload process has now a 2minutes timeout before being terminated forcefully. 10 second after pressing "Upload" the button comes pressable again, but this time the previous upload command gets killed explicitely --- app/src/processing/app/Editor.java | 29 +++++++++++++++++-- app/src/processing/app/EditorToolbar.java | 11 +++++-- .../src/cc/arduino/packages/Uploader.java | 13 +++++++-- .../packages/uploaders/SerialUploader.java | 8 ++++- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 8da903bcb..12560e5bf 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -146,6 +146,8 @@ public class Editor extends JFrame implements RunnerListener { private int numTools = 0; + public boolean avoidMultipleOperations = false; + private final EditorToolbar toolbar; // these menus are shared so that they needn't be rebuilt for all windows // each time a sketch is created, renamed, or moved. @@ -198,7 +200,7 @@ public class Editor extends JFrame implements RunnerListener { private Runnable stopHandler; Runnable exportHandler; private Runnable exportAppHandler; - + private Runnable timeoutUploadHandler; public Editor(Base ibase, File file, int[] storedLocation, int[] defaultLocation, Platform platform) throws Exception { super("Arduino"); @@ -1648,6 +1650,7 @@ public class Editor extends JFrame implements RunnerListener { stopHandler = new DefaultStopHandler(); exportHandler = new DefaultExportHandler(); exportAppHandler = new DefaultExportAppHandler(); + timeoutUploadHandler = new TimeoutUploadHandler(); } @@ -1979,6 +1982,7 @@ public class Editor extends JFrame implements RunnerListener { status.unprogress(); toolbar.deactivateRun(); + avoidMultipleOperations = false; } } @@ -2367,6 +2371,7 @@ public class Editor extends JFrame implements RunnerListener { console.clear(); status.progress(tr("Uploading to I/O Board...")); + new Thread(timeoutUploadHandler).start(); new Thread(usingProgrammer ? exportAppHandler : exportHandler).start(); } @@ -2406,6 +2411,7 @@ public class Editor extends JFrame implements RunnerListener { e.printStackTrace(); } finally { populatePortMenu(); + avoidMultipleOperations = false; } status.unprogress(); uploading = false; @@ -2500,6 +2506,7 @@ public class Editor extends JFrame implements RunnerListener { } catch (Exception e) { e.printStackTrace(); } finally { + avoidMultipleOperations = false; populatePortMenu(); } status.unprogress(); @@ -2514,6 +2521,20 @@ public class Editor extends JFrame implements RunnerListener { } } + class TimeoutUploadHandler implements Runnable { + + public void run() { + try { + //10 seconds, than reactivate upload functionality and let the programmer pid being killed + Thread.sleep(1000 * 10); + if (uploading) { + avoidMultipleOperations = false; + } + } catch (InterruptedException e) { + // noop + } + } + } public void handleSerial() { if(serialPlotter != null) { @@ -2558,7 +2579,7 @@ public class Editor extends JFrame implements RunnerListener { // If currently uploading, disable the monitor (it will be later // enabled when done uploading) - if (uploading) { + if (uploading || avoidMultipleOperations) { try { serialMonitor.suspend(); } catch (Exception e) { @@ -2582,8 +2603,10 @@ public class Editor extends JFrame implements RunnerListener { } try { - serialMonitor.open(); serialMonitor.setVisible(true); + if (!avoidMultipleOperations) { + serialMonitor.open(); + } success = true; } catch (ConnectException e) { statusError(tr("Unable to connect: is the sketch using the bridge?")); diff --git a/app/src/processing/app/EditorToolbar.java b/app/src/processing/app/EditorToolbar.java index a6b8bce60..a4d18b5f7 100644 --- a/app/src/processing/app/EditorToolbar.java +++ b/app/src/processing/app/EditorToolbar.java @@ -341,7 +341,10 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key switch (sel) { case RUN: - editor.handleRun(false, editor.presentHandler, editor.runHandler); + if (!editor.avoidMultipleOperations) { + editor.handleRun(false, editor.presentHandler, editor.runHandler); + editor.avoidMultipleOperations = true; + } break; // case STOP: @@ -370,7 +373,11 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key break; case EXPORT: - editor.handleExport(e.isShiftDown()); + // launch a timeout timer which can reenable to upload button functionality an + if (!editor.avoidMultipleOperations) { + editor.handleExport(e.isShiftDown()); + editor.avoidMultipleOperations = true; + } break; case SERIAL: diff --git a/arduino-core/src/cc/arduino/packages/Uploader.java b/arduino-core/src/cc/arduino/packages/Uploader.java index d58d29504..1d1967c36 100644 --- a/arduino-core/src/cc/arduino/packages/Uploader.java +++ b/arduino-core/src/cc/arduino/packages/Uploader.java @@ -44,6 +44,7 @@ import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.concurrent.TimeUnit; import static processing.app.I18n.tr; @@ -102,6 +103,9 @@ public abstract class Uploader implements MessageConsumer { return null; } + // static field for last executed programmer process ID + static protected Process programmerPid; + protected boolean executeUploadCommand(Collection command) throws Exception { return executeUploadCommand(command.toArray(new String[command.size()])); } @@ -121,11 +125,16 @@ public abstract class Uploader implements MessageConsumer { System.out.println(); } Process process = ProcessUtils.exec(command); + programmerPid = process; new MessageSiphon(process.getInputStream(), this, 100); new MessageSiphon(process.getErrorStream(), this, 100); - // wait for the process to finish. - result = process.waitFor(); + // wait for the process to finish, but not forever + // kill the flasher process after 2 minutes to avoid 100% cpu spinning + if (!process.waitFor(2, TimeUnit.MINUTES)) { + process.destroyForcibly(); + } + result = process.exitValue(); } catch (Exception e) { e.printStackTrace(); } diff --git a/arduino-core/src/cc/arduino/packages/uploaders/SerialUploader.java b/arduino-core/src/cc/arduino/packages/uploaders/SerialUploader.java index 971bfb8c6..64b66862d 100644 --- a/arduino-core/src/cc/arduino/packages/uploaders/SerialUploader.java +++ b/arduino-core/src/cc/arduino/packages/uploaders/SerialUploader.java @@ -78,6 +78,11 @@ public class SerialUploader extends Uploader { } prefs.putAll(targetPlatform.getTool(tool)); + if (programmerPid != null && programmerPid.isAlive()) { + // kill the previous programmer + programmerPid.destroyForcibly(); + } + // if no protocol is specified for this board, assume it lacks a // bootloader and upload using the selected programmer. if (usingProgrammer || prefs.get("upload.protocol") == null) { @@ -134,7 +139,7 @@ public class SerialUploader extends Uploader { // Scanning for available ports seems to open the port or // otherwise assert DTR, which would cancel the WDT reset if // it happened within 250 ms. So we wait until the reset should - // have already occured before we start scanning. + // have already occurred before we start scanning. actualUploadPort = waitForUploadPort(userSelectedUploadPort, before); } } catch (SerialException e) { @@ -213,6 +218,7 @@ public class SerialUploader extends Uploader { finalUploadPort = userSelectedUploadPort; } BaseNoGui.selectSerialPort(finalUploadPort); + return uploadResult; }