Skip to content

Commit c5ec507

Browse files
Refactor process termination logic: introduce ProcessUtil utility for reusable and cleaner code.
1 parent b93849d commit c5ec507

File tree

3 files changed

+74
-19
lines changed

3 files changed

+74
-19
lines changed

gui-app/src/main/kotlin/dev/robocode/tankroyale/gui/booter/BootProcess.kt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dev.robocode.tankroyale.gui.ui.console.BooterErrorConsole
88
import dev.robocode.tankroyale.gui.util.EDT
99
import dev.robocode.tankroyale.gui.util.FileUtil
1010
import dev.robocode.tankroyale.gui.util.ResourceUtil
11+
import dev.robocode.tankroyale.gui.util.ProcessUtil
1112
import java.io.BufferedReader
1213
import java.io.FileNotFoundException
1314
import java.io.InputStreamReader
@@ -155,14 +156,7 @@ object BootProcess {
155156
}
156157

157158
private fun stopProcess() {
158-
booterProcess?.apply {
159-
if (isAlive) {
160-
PrintStream(outputStream).apply {
161-
println("quit")
162-
flush()
163-
}
164-
}
165-
}
159+
ProcessUtil.stopProcess(booterProcess, "quit", false)
166160
booterProcess = null
167161
}
168162

gui-app/src/main/kotlin/dev/robocode/tankroyale/gui/server/ServerProcess.kt

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import dev.robocode.tankroyale.gui.ui.server.ServerLogFrame
88
import dev.robocode.tankroyale.gui.util.EDT
99
import dev.robocode.tankroyale.gui.util.FileUtil
1010
import dev.robocode.tankroyale.gui.util.ResourceUtil
11+
import dev.robocode.tankroyale.gui.util.ProcessUtil
1112
import java.io.BufferedReader
1213
import java.io.FileNotFoundException
1314
import java.io.InputStreamReader
14-
import java.io.PrintStream
1515
import java.nio.file.Files
1616
import java.nio.file.Paths
1717
import java.util.concurrent.atomic.AtomicBoolean
@@ -26,7 +26,7 @@ object ServerProcess {
2626
private val logThreadRunning = AtomicBoolean(false)
2727

2828
init {
29-
ServerActions
29+
ServerActions // trigger initialization of ServerEvents. Is not "unused"
3030
}
3131

3232
fun isRunning(): Boolean {
@@ -71,15 +71,7 @@ object ServerProcess {
7171
stopLogThread()
7272

7373
val process = processRef.get()
74-
process?.apply {
75-
if (isAlive) {
76-
PrintStream(outputStream).apply {
77-
println("q")
78-
flush()
79-
}
80-
}
81-
waitFor()
82-
}
74+
ProcessUtil.stopProcess(process, "q", true)
8375
processRef.set(null)
8476
logThread = null
8577

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package dev.robocode.tankroyale.gui.util
2+
3+
import java.io.PrintStream
4+
import java.util.concurrent.TimeUnit
5+
import java.util.logging.Level
6+
import java.util.logging.Logger
7+
8+
object ProcessUtil {
9+
private val logger = Logger.getLogger(ProcessUtil::class.java.name)
10+
11+
/**
12+
* Sends a quit command to the given process and optionally waits for it to exit.
13+
*
14+
* @param process The process to stop.
15+
* @param quitCommand The command string to send to the process' standard input to request shutdown.
16+
* @param waitForExit If true, waits for the process to terminate after sending the command.
17+
* @param forceTerminate If true, forcibly terminates the process if it doesn't exit within the timeout.
18+
* @return True, if the process was successfully stopped, false otherwise.
19+
*/
20+
@JvmStatic
21+
@JvmOverloads
22+
fun stopProcess(
23+
process: Process?,
24+
quitCommand: String,
25+
waitForExit: Boolean = false,
26+
forceTerminate: Boolean = false
27+
): Boolean {
28+
if (process == null || !process.isAlive) {
29+
return true // Nothing to do, a process is already stopped
30+
}
31+
32+
// Send quit command
33+
var quitCommandSent = false
34+
try {
35+
PrintStream(process.outputStream).use { ps ->
36+
ps.println(quitCommand)
37+
ps.flush()
38+
quitCommandSent = true
39+
}
40+
} catch (e: Exception) {
41+
logger.log(Level.WARNING, "Failed to send quit command to process", e)
42+
}
43+
44+
// If we don't need to wait, return whether the command was sent
45+
if (!waitForExit) {
46+
return quitCommandSent
47+
}
48+
49+
// Wait for a process to exit
50+
try {
51+
val exited = process.waitFor(500, TimeUnit.MILLISECONDS)
52+
53+
// If a process didn't exit and force termination is requested
54+
if (!exited && forceTerminate) {
55+
process.destroyForcibly()
56+
return process.waitFor(1000, TimeUnit.MILLISECONDS) // Give it 3 more seconds to terminate
57+
}
58+
return exited
59+
60+
} catch (_: InterruptedException) {
61+
Thread.currentThread().interrupt()
62+
logger.log(Level.WARNING, "Thread interrupted while waiting for process to exit")
63+
} catch (e: Exception) {
64+
logger.log(Level.WARNING, "Failed while waiting for process to exit", e)
65+
}
66+
67+
return !process.isAlive
68+
}
69+
}

0 commit comments

Comments
 (0)