Skip to content

Commit 9ad1dfb

Browse files
committed
WIP: No ConcurrentModificationExceptions anymore
1 parent 6183dfc commit 9ad1dfb

File tree

2 files changed

+47
-38
lines changed

2 files changed

+47
-38
lines changed

p5js/src/main/kotlin/p5jsEditor.kt

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import kotlinx.coroutines.CoroutineScope
1919
import kotlinx.coroutines.Dispatchers
2020
import kotlinx.coroutines.GlobalScope
2121
import kotlinx.coroutines.launch
22+
import kotlinx.coroutines.newSingleThreadContext
23+
import kotlinx.coroutines.sync.Mutex
24+
import kotlinx.coroutines.withContext
2225
import kotlinx.serialization.json.*
2326
import processing.app.*
2427
import processing.app.syntax.JEditTextArea
@@ -34,13 +37,14 @@ import java.io.InputStreamReader
3437
import java.net.URL
3538
import javax.swing.JMenu
3639
import javax.swing.JMenuItem
40+
import kotlin.jvm.optionals.getOrNull
3741

3842

3943
class p5jsEditor(base: Base, path: String?, state: EditorState?, mode: Mode?): Editor(base, path, state, mode) {
4044

4145
val scope = CoroutineScope(Dispatchers.Default)
4246
val isWindows = System.getProperty("os.name").lowercase().contains("windows")
43-
val shell = System.getenv("SHELL")
47+
var sketchProcess: Process? = null
4448

4549
init {
4650
scope.launch {
@@ -96,15 +100,15 @@ class p5jsEditor(base: Base, path: String?, state: EditorState?, mode: Mode?): E
96100
}
97101

98102
statusNotice("Installing Node via pnpm…")
99-
runCommand("pnpm env use --global lts", onFinished = {
103+
runCommand("pnpm env use --global lts") {
100104
statusNotice("Installing Node dependencies…")
101-
})
105+
}
102106
}
103107

104108
// --dangerously-allow-all-builds allows electron in particular to install properly
105-
runCommand("pnpm install --dangerously-allow-all-builds", onFinished = {
109+
runCommand("pnpm install --dangerously-allow-all-builds") {
106110
statusNotice("All done! Enjoy p5.js mode.")
107-
})
111+
}
108112
}
109113
}
110114

@@ -144,10 +148,10 @@ class p5jsEditor(base: Base, path: String?, state: EditorState?, mode: Mode?): E
144148
runCommand("pnpm install --dangerously-allow-all-builds --force")
145149
}
146150

147-
runCommand("pnpm app:pack", onFinished = {
151+
runCommand("pnpm app:pack") {
148152
Platform.openFolder(sketch.folder)
149153
statusNotice(Language.text("export.notice.exporting.done"))
150-
})
154+
}
151155
}
152156

153157
// override fun handleSaveAs(): Boolean {
@@ -181,11 +185,11 @@ class p5jsEditor(base: Base, path: String?, state: EditorState?, mode: Mode?): E
181185
}
182186

183187
override fun internalCloseRunner() {
184-
processes.forEach { it.destroy() }
188+
sketchProcess?.destroy()
185189
}
186190

187191
override fun deactivateRun() {
188-
processes.forEach { it.destroy() }
192+
sketchProcess?.destroy()
189193
toolbar.deactivateRun()
190194
}
191195

@@ -254,27 +258,31 @@ class p5jsEditor(base: Base, path: String?, state: EditorState?, mode: Mode?): E
254258

255259
}
256260

257-
// TODO: state is maintained => turn into class
258-
val processes = mutableListOf<Process>()
259261
fun runCommand(action: String, directory: File = sketch.folder, onFinished: () -> Unit = {}) {
260-
// Wait for previous processes to finish
261-
processes.forEach { it.waitFor() }
262+
try {
263+
// TODO: Get rid of magic strings. Better way to distinguish “endless” processes and processes we wait for?
264+
if (action == "pnpm sketch:start") {
265+
sketchProcess?.destroy()
266+
}
262267

263-
val processBuilder = ProcessBuilder()
268+
val processBuilder = ProcessBuilder()
264269

265-
// Set the command based on the operating system
266-
val command = if (isWindows) {
267-
listOf("cmd", "/c", action)
268-
} else {
269-
listOf(shell, "-ci", action)
270-
}
270+
// Set the command based on the operating system
271+
val shell = System.getenv("SHELL")
272+
val command = if (isWindows) {
273+
listOf("cmd", "/c", action)
274+
} else {
275+
listOf(shell, "-ci", action)
276+
}
271277

272-
processBuilder.command(command)
273-
processBuilder.directory(directory)
278+
processBuilder.command(command)
279+
processBuilder.directory(directory)
274280

275-
try {
276281
val process = processBuilder.start()
277-
processes.add(process)
282+
283+
if (action == "pnpm sketch:start") {
284+
sketchProcess = process;
285+
}
278286

279287
// Handle output stream
280288
val reader = BufferedReader(InputStreamReader(process.inputStream))
@@ -294,15 +302,15 @@ class p5jsEditor(base: Base, path: String?, state: EditorState?, mode: Mode?): E
294302
println(line)
295303
}
296304

297-
// Wait for the process to complete
298305
val exitCode = process.waitFor()
299-
processes.remove(process)
300-
onFinished()
306+
301307
if (exitCode != 0) {
302-
throw RuntimeException("$action failed with exit code $exitCode.")
308+
throw RuntimeException("Command failed with non-zero exit code $exitCode.")
303309
}
310+
311+
onFinished()
304312
} catch (e: Exception) {
305-
throw RuntimeException("Failed to run $action.", e)
313+
statusError("Failed to run `$action`: ${e.message}")
306314
}
307315
}
308316
}
Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
package processing.p5js
22

3+
import kotlinx.coroutines.CoroutineScope
34
import kotlinx.coroutines.launch
4-
import processing.app.Messages
5-
import processing.app.ui.Editor
5+
import kotlinx.coroutines.sync.withLock
66
import processing.app.ui.EditorToolbar
77

8-
class p5jsEditorToolbar(editor: p5jsEditor?) : EditorToolbar(editor) {
8+
class p5jsEditorToolbar(editor: p5jsEditor) : EditorToolbar(editor) {
99
override fun handleRun(modifiers: Int) {
1010
val editor = editor as p5jsEditor
1111

12-
editor.scope.launch {
13-
editor.sketch.save()
12+
editor.sketch.save()
13+
activateRun()
14+
editor.statusNotice("Starting up sketch…")
1415

15-
runButton.setSelected(true)
16-
editor.statusNotice("Starting up sketch…")
16+
editor.scope.launch {
1717
editor.runCommand("pnpm sketch:start") {
18-
runButton.setSelected(false)
18+
deactivateRun()
19+
editor.statusEmpty()
1920
}
2021
}
2122
}
2223

2324
override fun handleStop() {
2425
val editor = editor as p5jsEditor
25-
editor.processes.forEach { it.destroy() }
26+
editor.sketchProcess?.destroy()
2627
deactivateRun()
2728
}
2829
}

0 commit comments

Comments
 (0)