From 7e0ab9a79e6068baa712024827a9e0b3f4545497 Mon Sep 17 00:00:00 2001 From: znzryb Date: Sun, 30 Nov 2025 22:11:21 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=9A=20fix:=20Prevent=20multiple=20prog?= =?UTF-8?q?ress=20bars=20when=20receiving?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gather/base/ProblemGatheringBridge.kt | 57 +++++++++++++++++-- src/main/resources/META-INF/plugin.xml | 3 +- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/github/pushpavel/autocp/gather/base/ProblemGatheringBridge.kt b/src/main/kotlin/com/github/pushpavel/autocp/gather/base/ProblemGatheringBridge.kt index 7c8584f8..5b24bc14 100644 --- a/src/main/kotlin/com/github/pushpavel/autocp/gather/base/ProblemGatheringBridge.kt +++ b/src/main/kotlin/com/github/pushpavel/autocp/gather/base/ProblemGatheringBridge.kt @@ -3,11 +3,17 @@ package com.github.pushpavel.autocp.gather.base import com.github.pushpavel.autocp.common.helpers.ioScope import com.github.pushpavel.autocp.common.res.R import com.github.pushpavel.autocp.gather.models.ProblemJson +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project import com.intellij.openapi.startup.ProjectActivity +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json @@ -15,21 +21,41 @@ import java.net.SocketTimeoutException /** * Bridge between Competitive Companion extension and [BatchProcessor] + * Application-level service to ensure only one instance is running */ -class ProblemGatheringBridge : ProjectActivity, DumbAware { +@Service(Service.Level.APP) +class ProblemGatheringBridge : Disposable { private val scope = ioScope() private val serializer = Json { ignoreUnknownKeys = true } + + @Volatile + private var isRunning = false + + @Volatile + private var serverJob: Job? = null - fun runActivity(project: Project) { + companion object { + fun getInstance(): ProblemGatheringBridge { + return ApplicationManager.getApplication().getService(ProblemGatheringBridge::class.java) + } + } + + fun start() { + if (isRunning) { + return + } + + isRunning = true + // initialize server - scope.launch { + serverJob = scope.launch { try { val serverSocket = openServerSocketAsync( R.others.competitiveCompanionPorts ).await() ?: throw ProblemGatheringErr.AllPortsTakenErr(R.others.competitiveCompanionPorts) serverSocket.use { - while (true) { + while (isActive) { try { coroutineScope { val message = listenForMessageAsync( @@ -51,12 +77,31 @@ class ProblemGatheringBridge : ProjectActivity, DumbAware { } catch (e: ProblemGatheringErr) { R.notify.problemGatheringErr(e) } catch (e: Exception) { - R.notify.problemGatheringUncaught(e) + if (e !is CancellationException) { + R.notify.problemGatheringUncaught(e) + } } finally { + isRunning = false BatchProcessor.interruptBatch() } } } - override suspend fun execute(project: Project) = runActivity(project) + fun stop() { + serverJob?.cancel() + isRunning = false + } + + override fun dispose() { + stop() + } +} + +/** + * Starts the ProblemGatheringBridge service when the first project opens + */ +class ProblemGatheringBridgeStarter : ProjectActivity, DumbAware { + override suspend fun execute(project: Project) { + ProblemGatheringBridge.getInstance().start() + } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 873a4b4d..a3b943e0 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -53,7 +53,8 @@ - + +