From 81740e0418c61687df82e1d6522450576ad3efb5 Mon Sep 17 00:00:00 2001 From: Luca Raddatz Date: Wed, 13 Nov 2024 23:51:20 +0100 Subject: [PATCH] Fixed killing jcef processes --- .../aqua/bgw/application/JCEFApplication.kt | 51 ++++++++++++- ...a.bgw.multiplatform-conventions.gradle.kts | 76 +++---------------- 2 files changed, 59 insertions(+), 68 deletions(-) diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt index 335863ec8..27cf5e16e 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt @@ -7,6 +7,10 @@ import InternalCameraPaneData import data.event.* import data.event.internal.* import jsonMapper +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlinx.serialization.ExperimentalSerializationApi import mapper.DialogMapper import me.friwi.jcefmaven.CefAppBuilder @@ -45,10 +49,15 @@ import java.awt.EventQueue import java.awt.KeyboardFocusManager import java.awt.event.WindowAdapter import java.awt.event.WindowEvent +import java.io.File +import java.lang.management.ManagementFactory import java.net.ServerSocket +import java.util.stream.Collectors +import java.util.stream.Stream import javax.imageio.ImageIO import javax.swing.JFrame import javax.swing.WindowConstants.EXIT_ON_CLOSE +import kotlin.concurrent.timer import kotlin.system.exitProcess @@ -192,7 +201,7 @@ class JCEFApplication : Application { } -@OptIn(ExperimentalSerializationApi::class) +@OptIn(ExperimentalSerializationApi::class, DelicateCoroutinesApi::class) class MainFrame( startURL: String = "http://localhost", useOSR: Boolean = false, @@ -227,7 +236,8 @@ class MainFrame( }) val cefApp = builder.build() - + val pids = mutableSetOf() + var pidsUnchanged = 0 val platform = EnumPlatform.getCurrentPlatform() println(platform) @@ -330,10 +340,47 @@ class MainFrame( override fun windowOpened(e: WindowEvent?) { super.windowOpened(e) + + println("Application is running with PID: ${ManagementFactory.getRuntimeMXBean().name}") + timer(period = 1000) { + val pidsBefore = pids.size + pids += filterJCEFHelperProcesses(getChildProcessIds()) + if(pids.size == pidsBefore) { + pidsUnchanged++ + } else { + pidsUnchanged = 0 + } + + if(pidsUnchanged >= 2) { + println("JCEF Helper PIDs: $pids") + File("build/application.pid").writeText(pids.joinToString(",").trim()) + this.cancel() + } + } } }) } + internal fun getChildProcessIds(process: ProcessHandle = ProcessHandle.current()): Set { + val childrenList = mutableListOf() + val children = process.children().let { + it.forEach { childrenList.add(it) } + childrenList + } + return (children.flatMap { getChildProcessIds(it) } + children.map { it.pid() }).toSet() + } + + internal fun getChildrenJCEFHelperProcesses(): Set { + return ProcessHandle.allProcesses() + .filter { it.info().command().orElse("").contains("jcef_helper") } + .map { it.pid() } + .collect(Collectors.toSet()) + } + + internal fun filterJCEFHelperProcesses(pids: Set): Set { + return pids.filter { it in getChildrenJCEFHelperProcesses() }.toSet() + } + fun setDialogContent(browser : CefBrowser, dialogData : DialogData) { browser.executeJavaScript( """ diff --git a/buildSrc/src/main/kotlin/tools.aqua.bgw.multiplatform-conventions.gradle.kts b/buildSrc/src/main/kotlin/tools.aqua.bgw.multiplatform-conventions.gradle.kts index 1dca12cb5..8d4e3e8b4 100644 --- a/buildSrc/src/main/kotlin/tools.aqua.bgw.multiplatform-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/tools.aqua.bgw.multiplatform-conventions.gradle.kts @@ -163,83 +163,27 @@ tasks.named("jvmProcessResources") { from(jsBrowserDistribution) } -var initialHelperPIDs = setOf() - -// Cleanup task that depends on tracking `jcef_helper.exe` processes -val cleanupJcefHelper by - tasks.registering { - group = "application" - description = "Cleans up orphaned jcef_helper processes after the run task" - - doLast { - // After 'run' completes, find new `jcef_helper.exe` processes and terminate them - println("Initial jcef_helper PIDs: $initialHelperPIDs") - val currentHelperPIDs = getCurrentJcefHelperPIDs() - println("Current jcef_helper PIDs: $currentHelperPIDs") - val newHelperPIDs = currentHelperPIDs - initialHelperPIDs - println("New jcef_helper PIDs: $newHelperPIDs") - - println(newHelperPIDs.map { - getParentProcessId(it) - }) - - // Kill only new helper processes started during the run - killJcefHelperProcesses(newHelperPIDs) - } - } - tasks.named("run") { - doFirst { - initialHelperPIDs = getCurrentJcefHelperPIDs() - println("Initial jcef_helper PIDs: $initialHelperPIDs") - } - dependsOn(tasks.named("jvmJar")) classpath(tasks.named("jvmJar")) - - doLast { - val currentHelperPIDs = getCurrentJcefHelperPIDs() - println("After jcef_helper PIDs: $currentHelperPIDs") - println(currentHelperPIDs.map { - getParentProcessId(it) - }) - } } +// TODO - Somehow invoke this function from implementations of dependency gradle.buildFinished { - println("Build finished, cleaning up jcef_helper processes") - tasks.named("cleanupJcefHelper").get().actions.forEach { - it.execute(tasks.named("cleanupJcefHelper").get()) - } -} - -// Helper function to get current jcef_helper process IDs -fun getCurrentJcefHelperPIDs(): Set { - return ProcessHandle.allProcesses() - .filter { it.info().command().orElse("").contains("jcef_helper") } - .map { it.pid() } - .collect(Collectors.toSet()) -} - -// Function to get the parent process ID of a given process -fun getParentProcessId(pid: Long): Long? { - val osBean = ManagementFactory.getOperatingSystemMXBean() as OperatingSystemMXBean - val processHandle = ProcessHandle.of(pid) - return processHandle.orElse(null)?.parent()?.orElse(null)?.pid() + try { + val applicationPIDs = file("build/application.pid").readText().split(",").map { it.toLong() }.toSet() + println("Created JCEF_Helper PIDs: $applicationPIDs") + killJcefHelperProcesses(applicationPIDs) + } catch (e: Exception) {} } // Function to kill JCEF helper processes fun killJcefHelperProcesses(pids: Set) { - val currentProcessId = ProcessHandle.current().pid() pids.forEach { pid -> - val parentPid = getParentProcessId(pid) - println("Parent process ID of $pid is $parentPid") - if (parentPid == currentProcessId) { - println("Killing process $pid which is a child of the current process $currentProcessId") - Runtime.getRuntime().exec("kill -9 $pid") - } else { - println("Skipping process $pid as it is not a child of the current process $currentProcessId") - } + ProcessHandle.of(pid).ifPresent { + println("Killing process $pid") + it.destroy() + } } }