Skip to content

Commit ff2fd0e

Browse files
committed
python running concurrently and builds are lightening-fast!
1 parent 3f29cb4 commit ff2fd0e

File tree

7 files changed

+602
-553
lines changed

7 files changed

+602
-553
lines changed

release/PythonMode.zip

-1.14 KB
Binary file not shown.

src/info/sansgills/mode/python/Communicator.java

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,28 @@
1717
*/
1818

1919
public class Communicator {
20-
private PythonEditor editor;
20+
private PythonRunner runner;
2121
private Process sketchProcess;
2222

2323
private StreamRedirectThread outThread;
2424
private MessageReceiverThread errThread;
2525

2626
private PrintWriter toSketch;
2727

28-
public Communicator (Process sketchProcess, PythonEditor editor){
28+
public Communicator (Process sketchProcess, PythonRunner runner){
2929
this.sketchProcess = sketchProcess;
30-
this.editor = editor;
30+
this.runner = runner;
3131

3232
outThread = new StreamRedirectThread("JVM Stdout Reader", sketchProcess.getInputStream(), System.out);
33-
errThread = new MessageReceiverThread(sketchProcess.getErrorStream(), editor);
33+
errThread = new MessageReceiverThread(sketchProcess.getErrorStream(), runner);
3434

3535
toSketch = new PrintWriter(sketchProcess.getOutputStream());
3636

3737
outThread.start();
3838
errThread.start();
3939
}
4040

41-
public void close(){
41+
public void destroy(){
4242
errThread.running = false;
4343
errThread = null;
4444
outThread = null;
@@ -54,6 +54,7 @@ public void close(){
5454
*/
5555
public void sendClose(){
5656
toSketch.println("__STOP__"); //hard-coded, what the hell
57+
System.out.println("__STOP__");
5758
toSketch.flush();
5859
}
5960

@@ -68,19 +69,20 @@ public void sendSketch(String[] args){
6869

6970

7071
toSketch.println(out.toString());
71-
72+
System.out.println(out.toString());
73+
toSketch.flush();
7274
}
7375

7476
//private class to handle doing things when the sketch process sends us a message via system.err
7577
private class MessageReceiverThread extends Thread{
76-
PythonEditor editor;
78+
PythonRunner runner;
7779
BufferedReader messageReader;
7880

7981
public boolean running;
8082

81-
public MessageReceiverThread(InputStream messageStream, PythonEditor editor){
83+
public MessageReceiverThread(InputStream messageStream, PythonRunner runner){
8284
this.messageReader = new BufferedReader(new InputStreamReader(messageStream));
83-
this.editor = editor;
85+
this.runner = runner;
8486
this.running = true;
8587
}
8688

@@ -90,19 +92,13 @@ public void run() {
9092

9193
// continually read messages
9294
while ((currentLine = messageReader.readLine()) != null && running) {
93-
if (currentLine.indexOf(PApplet.EXTERNAL_STOP) == 0) {
95+
if (currentLine.indexOf("__STOPPED__") != -1) {
9496
// sketch telling us it stopped
95-
editor.internalCloseRunner();
96-
return;
97-
}else if (currentLine.indexOf(PApplet.EXTERNAL_MOVE) == 0) {
98-
//sketch telling us it moved
99-
String nums = currentLine.substring(currentLine.indexOf(' ') + 1).trim();
100-
int space = nums.indexOf(' ');
101-
int left = Integer.parseInt(nums.substring(0, space));
102-
int top = Integer.parseInt(nums.substring(space + 1));
103-
editor.setSketchLocation(new Point(left, top));
97+
runner.parallelStopped();
10498
return;
105-
}else{
99+
}else if(currentLine.indexOf("__STARTED__") != -1){
100+
runner.parallelStarted();
101+
} else{
106102
System.err.println(currentLine);
107103
}
108104
}

src/info/sansgills/mode/python/PythonEditor.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class PythonEditor extends Editor {
3434
protected PythonEditor(final Base base, String path, EditorState state, final Mode mode) {
3535
super(base, path, state, mode);
3636

37+
runner = new PythonRunner(this);
38+
3739
listener = new PythonKeyListener(this, textarea); //black magic
3840
pyMode = (PythonMode) mode; //convenience
3941
}
@@ -132,7 +134,6 @@ public void handleExportApplication() {
132134

133135
//Note that I'm doing the build here instead of in PythonMode, because of simplicity
134136
public void handleRun() {
135-
final PythonEditor self = this; //there must be a more elegant way to do this.
136137
new Thread(new Runnable(){
137138
public void run(){
138139
toolbar.activate(PythonToolbar.RUN); //pretty lights
@@ -143,14 +144,12 @@ public void run(){
143144
} catch (Exception e){
144145
statusError(e); //do something pretty?
145146
}
146-
runner = new PythonRunner(build, self); //create runtime (can't use 'this', this is a runnable)
147-
runner.launch(false); //launch runtime; present = false
147+
runner.launch(build, false); //launch runtime; present = false
148148
}
149149
}).start();
150150
}
151151

152152
public void handlePresent() {
153-
final PythonEditor self = this;
154153
new Thread(new Runnable() {
155154
public void run() {
156155
toolbar.activate(PythonToolbar.RUN);
@@ -161,19 +160,15 @@ public void run() {
161160
} catch (Exception e){
162161
statusError(e);
163162
}
164-
runner = new PythonRunner(build, self);
165-
runner.launch(true); //present = true
163+
runner.launch(build, true); //present = true
166164
}
167165
}).start();
168166
}
169167

170168
public void handleStop() { //copied wholesale from Java Mode
171169
toolbar.activate(PythonToolbar.STOP);
172170
try {
173-
if (runner != null) {
174-
runner.close(); // kills the window
175-
runner = null;
176-
}
171+
runner.internalClose();
177172
} catch (Exception e) {
178173
statusError(e);
179174
}

src/info/sansgills/mode/python/PythonRunner.java

Lines changed: 97 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -27,50 +27,83 @@ public class PythonRunner {
2727
// Threads to redirect output / error streams from process to us
2828
Communicator communicator;
2929

30-
public PythonRunner(PythonBuild build, PythonEditor editor) {
31-
this.build = build;
30+
boolean active;
31+
32+
public PythonRunner(PythonEditor editor) {
3233
this.editor = editor;
34+
active = false;
35+
36+
//make sure our partner process is dead
37+
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
38+
public void run() {
39+
if (communicator != null) {
40+
communicator.destroy();
41+
}
42+
if(sketchProcess != null) {
43+
sketchProcess.destroy();
44+
}
45+
}
46+
}));
3347
}
34-
48+
3549
/*
3650
* Run the code.
3751
*/
38-
public void launch(boolean present) {
39-
exec(buildArgs(present));
52+
public void launch(PythonBuild build, boolean present) {
53+
this.build = build;
54+
ensureParallel();
55+
communicator.sendSketch(buildSketchArgs(present));
56+
}
57+
58+
/*
59+
* Kill the code.
60+
*/
61+
public void internalClose() {
62+
//Closed from editor button
63+
ensureParallel();
64+
System.out.println("Closing...");
65+
communicator.sendClose();
4066
}
4167

4268
/*
43-
* Construct the command-line command to start the sketch with
44-
*
45-
* TODO add proper machine detection & whatnot from Java Mode
46-
*
69+
* Make sure we've got a process to run the code.
4770
*/
48-
private String[] buildArgs(boolean present){
49-
ArrayList<String> args = new ArrayList<String>();
50-
51-
// Manage java
52-
appendJavaArgs(args);
53-
54-
// Manage Python Mode
55-
args.add("-cp");
56-
args.add(build.getClassPath());
57-
58-
args.add("info.sansgills.mode.python.wrapper.ProcessingJythonWrapper"); // main class
59-
60-
args.add(build.getResultFile()); //path to script
61-
62-
appendSketchArgs(args, present);
63-
64-
String[] out = args.toArray(new String[0]);
65-
66-
return out;
71+
private void ensureParallel(){
72+
if(sketchProcess == null){
73+
if(build == null){
74+
System.err.println("need a build");
75+
return;
76+
}
77+
sketchProcess = PApplet.exec(buildJavaArgs());
78+
communicator = new Communicator(sketchProcess, this);
79+
}
6780
}
68-
81+
82+
83+
public void parallelStopped() {
84+
//Closed from sketch window
85+
if(active){
86+
active = false;
87+
System.out.println("Closed");
88+
}else{
89+
System.err.println("something is wrong");
90+
}
91+
}
92+
public void parallelStarted(){
93+
if(!active){
94+
active = true;
95+
}else{
96+
System.err.println("something is very wrong");
97+
}
98+
}
99+
69100
/*
70-
* Create the proper args to run the jvm with
101+
* Command to start the companion process
71102
*/
72-
private void appendJavaArgs(ArrayList<String> args) {
103+
private String[] buildJavaArgs() {
104+
ArrayList<String> args = new ArrayList<String>();
73105

106+
// Manage java
74107
// special handling for base command for OS X- from Java Mode
75108
if (!Base.isMacOS()) {
76109
args.add("java");
@@ -97,7 +130,7 @@ private void appendJavaArgs(ArrayList<String> args) {
97130
}
98131
}
99132
}
100-
133+
101134
// Memory
102135
if (Preferences.getBoolean("run.options.memory")) {
103136
args.add("-Xms" + Preferences.get("run.options.memory.initial") + "m");
@@ -108,69 +141,48 @@ private void appendJavaArgs(ArrayList<String> args) {
108141
if (Base.isMacOS()) {
109142
args.add("-Xdock:name=" + build.getClassName());
110143
}
111-
144+
112145
// Path to the libraryies we use
113146
// TODO what's the difference between classpath and library path?
114-
args.add("-Djava.library.path="
115-
+ build.getJavaLibraryPath()
116-
+ File.pathSeparator
117-
+ System.getProperty("java.library.path"));
147+
args.add("-Djava.library.path=" + build.getJavaLibraryPath()
148+
+ File.pathSeparator + System.getProperty("java.library.path"));
149+
150+
// Manage Python Mode
151+
args.add("-cp");
152+
args.add(build.getClassPath());
153+
154+
args.add("info.sansgills.mode.python.wrapper.ProcessingJythonWrapper"); // main class
155+
156+
//we parallel
157+
args.add("--parallel");
158+
159+
return args.toArray(new String[0]);
118160

119161
}
120162

121163
/*
122-
* Sketch-specific stuff
164+
* Arguments for individual sketches
123165
*/
124-
private void appendSketchArgs(ArrayList<String> args, boolean present){
125-
// place the sketch
126-
// TODO handle multiple displays
127-
Point windowLocation = editor.getSketchLocation();
128-
if (windowLocation != null) {
129-
// saved location - sketch run more than once
130-
args.add(PApplet.ARGS_LOCATION
131-
+ "="
132-
+ windowLocation.x + "," + windowLocation.y);
133-
} else {
134-
// tell PApplet where the editor is and let it sort itself out
135-
Point editorLocation = editor.getLocation();
136-
args.add(PApplet.ARGS_EDITOR_LOCATION
137-
+ "="
138-
+ editorLocation.x + "," + editorLocation.y);
139-
}
166+
private String[] buildSketchArgs(boolean present) {
167+
ArrayList<String> args = new ArrayList<String>();
168+
169+
args.add("--script="+build.getResultFile()); // path to script
170+
171+
// tell PApplet where the editor is and let it sort itself out
172+
Point editorLocation = editor.getLocation();
173+
args.add(PApplet.ARGS_EDITOR_LOCATION + "=" + editorLocation.x + ","
174+
+ editorLocation.y);
140175

141176
if (present) {
142177
args.add(PApplet.ARGS_FULL_SCREEN);
143-
args.add(PApplet.ARGS_STOP_COLOR + "=" + Preferences.get("run.present.stop.color"));
144-
args.add(PApplet.ARGS_BGCOLOR + "=" + Preferences.get("run.present.bgcolor"));
178+
args.add(PApplet.ARGS_STOP_COLOR + "="
179+
+ Preferences.get("run.present.stop.color"));
180+
args.add(PApplet.ARGS_BGCOLOR + "="
181+
+ Preferences.get("run.present.bgcolor"));
145182
}
146-
147-
148-
args.add(PApplet.ARGS_EXTERNAL);
149-
args.add(build.getClassName()); // sketch name
150-
}
151-
152-
/*
153-
* Start the process & a thread to track it
154-
*/
155-
private void exec(final String[] args){
156-
new Thread(new Runnable(){
157-
public void run(){
158-
sketchProcess = PApplet.exec(args);
159-
communicator = new Communicator(sketchProcess, editor);
160-
try{
161-
int result = sketchProcess.waitFor();
162-
}catch(InterruptedException e){
163-
System.out.println("error:");
164-
e.printStackTrace();
165-
}
166-
}
167-
}).start();
168-
}
169-
170-
/*
171-
* Kill the code.
172-
*/
173-
public void close() {
174-
communicator.sendClose();
183+
184+
args.add(build.getClassName()); // sketch name MUST BE LAST
185+
186+
return args.toArray(new String[0]);
175187
}
176188
}

0 commit comments

Comments
 (0)