Skip to content

Commit 35848e0

Browse files
committed
Mitigated Serial Monitor resource exhaustion when the connected device sends a lot of data
Fixes arduino#2233
1 parent 1be99c3 commit 35848e0

File tree

3 files changed

+120
-13
lines changed

3 files changed

+120
-13
lines changed

app/src/processing/app/SerialMonitor.java

+39-13
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,29 @@
1919
package processing.app;
2020

2121
import processing.app.debug.MessageConsumer;
22+
import processing.app.debug.TextAreaFIFO;
2223
import processing.core.*;
2324
import static processing.app.I18n._;
2425

2526
import java.awt.*;
2627
import java.awt.event.*;
2728
import javax.swing.*;
2829
import javax.swing.border.*;
29-
import javax.swing.event.*;
3030
import javax.swing.text.*;
3131

32-
public class SerialMonitor extends JFrame implements MessageConsumer {
32+
public class SerialMonitor extends JFrame implements MessageConsumer,ActionListener {
3333
private Serial serial;
3434
private String port;
35-
private JTextArea textArea;
35+
private TextAreaFIFO textArea;
3636
private JScrollPane scrollPane;
3737
private JTextField textField;
3838
private JButton sendButton;
3939
private JCheckBox autoscrollBox;
4040
private JComboBox lineEndings;
4141
private JComboBox serialRates;
4242
private int serialRate;
43+
private javax.swing.Timer updateTimer;
44+
private StringBuffer updateBuffer;
4345

4446
public SerialMonitor(String port) {
4547
super(port);
@@ -67,7 +69,9 @@ public void actionPerformed(ActionEvent e) {
6769
Font editorFont = Preferences.getFont("editor.font");
6870
Font font = new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize());
6971

70-
textArea = new JTextArea(16, 40);
72+
textArea = new TextAreaFIFO(4000000);
73+
textArea.setRows(16);
74+
textArea.setColumns(40);
7175
textArea.setEditable(false);
7276
textArea.setFont(font);
7377

@@ -171,6 +175,9 @@ public void actionPerformed(ActionEvent event) {
171175
}
172176
}
173177
}
178+
179+
updateBuffer = new StringBuffer(1048576);
180+
updateTimer = new javax.swing.Timer(33, this); // redraw serial monitor at 30 Hz
174181
}
175182

176183
protected void setPlacement(int[] location) {
@@ -203,9 +210,9 @@ private void send(String s) {
203210

204211
public void openSerialPort() throws SerialException {
205212
if (serial != null) return;
206-
207213
serial = new Serial(port, serialRate);
208214
serial.addListener(this);
215+
updateTimer.start();
209216
}
210217

211218
public void closeSerialPort() {
@@ -219,13 +226,32 @@ public void closeSerialPort() {
219226
}
220227
}
221228

222-
public void message(final String s) {
223-
SwingUtilities.invokeLater(new Runnable() {
224-
public void run() {
225-
textArea.append(s);
226-
if (autoscrollBox.isSelected()) {
227-
textArea.setCaretPosition(textArea.getDocument().getLength());
228-
}
229-
}});
229+
public void message(String s) {
230+
// TODO: can we pass a byte array, to avoid overhead of String
231+
addToUpdateBuffer(s);
230232
}
233+
234+
private synchronized void addToUpdateBuffer(String s) {
235+
updateBuffer.append(s);
236+
}
237+
238+
private synchronized String consumeUpdateBuffer() {
239+
String s = updateBuffer.toString();
240+
updateBuffer.setLength(0);
241+
return s;
242+
}
243+
244+
public void actionPerformed(ActionEvent e) {
245+
final String s = consumeUpdateBuffer();
246+
if (s.length() > 0) {
247+
//System.out.println("gui append " + s.length());
248+
boolean scroll = autoscrollBox.isSelected();
249+
textArea.allowTrim(scroll);
250+
textArea.append(s);
251+
if (scroll) {
252+
textArea.setCaretPosition(textArea.getDocument().getLength());
253+
}
254+
}
255+
}
256+
231257
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Copyright (c) 2014 Paul Stoffregen <[email protected]>
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program; if not, write to the Free Software Foundation,
16+
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+
18+
$Id$
19+
*/
20+
21+
// adapted from https://community.oracle.com/thread/1479784
22+
23+
package processing.app.debug;
24+
25+
import javax.swing.JTextArea;
26+
import javax.swing.SwingUtilities;
27+
import javax.swing.event.DocumentEvent;
28+
import javax.swing.event.DocumentListener;
29+
import javax.swing.text.BadLocationException;
30+
31+
public class TextAreaFIFO extends JTextArea implements DocumentListener {
32+
private int maxChars;
33+
34+
private int updateCount; // limit how often we trim the document
35+
36+
private boolean doTrim;
37+
38+
public TextAreaFIFO(int max) {
39+
maxChars = max;
40+
updateCount = 0;
41+
doTrim = true;
42+
getDocument().addDocumentListener(this);
43+
}
44+
45+
public void allowTrim(boolean trim) {
46+
doTrim = trim;
47+
}
48+
49+
public void insertUpdate(DocumentEvent e) {
50+
if (++updateCount > 150 && doTrim) {
51+
updateCount = 0;
52+
SwingUtilities.invokeLater(new Runnable() {
53+
public void run() {
54+
trimDocument();
55+
}
56+
});
57+
}
58+
}
59+
60+
public void removeUpdate(DocumentEvent e) {
61+
}
62+
63+
public void changedUpdate(DocumentEvent e) {
64+
}
65+
66+
public void trimDocument() {
67+
int len = 0;
68+
len = getDocument().getLength();
69+
if (len > maxChars) {
70+
int n = len - maxChars;
71+
System.out.println("trimDocument: remove " + n + " chars");
72+
try {
73+
getDocument().remove(0, n);
74+
} catch (BadLocationException ble) {
75+
}
76+
}
77+
}
78+
}

build/shared/revisions.txt

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ ARDUINO 1.0.7
1313
* Fixed missing NOT_AN_INTERRUPT constant in digitalPinToInterrupt() macro
1414
* Fixed performance regression in HardwareSerial::available() introduced with https://github.com/arduino/Arduino/pull/2057
1515

16+
[ide]
17+
* Mitigated Serial Monitor resource exhaustion when the connected device sends a lot of data (Paul Stoffregen)
18+
1619
ARDUINO 1.0.6 - 2014.09.16
1720

1821
[core]

0 commit comments

Comments
 (0)