Skip to content

Commit cee7d30

Browse files
committed
fixe : resolve EDT freeze and thread safety issues in JetBrains plugin
1 parent 0f2a08f commit cee7d30

4 files changed

Lines changed: 27 additions & 26 deletions

File tree

jetbrains-plugin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
}
66

77
group = "xyz.devglobe"
8-
version = "1.1.0"
8+
version = "1.1.1"
99

1010
repositories {
1111
mavenCentral()

jetbrains-plugin/src/main/kotlin/xyz/devglobe/plugin/DevGlobeStartupActivity.kt

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
11
package xyz.devglobe.plugin
22

33
import com.intellij.openapi.project.Project
4-
import com.intellij.openapi.startup.StartupActivity
4+
import com.intellij.openapi.startup.ProjectActivity
55
import xyz.devglobe.plugin.auth.ApiKeyStorage
66
import xyz.devglobe.plugin.core.DevGlobeTracker
77
import xyz.devglobe.plugin.settings.DevGlobeSettings
88

9-
/**
10-
* Runs once per project open.
11-
* Initializes the tracker if an API key is stored and tracking is enabled.
12-
* Since the tracker is application-level (singleton), multiple project opens
13-
* are safe — the tracker ignores redundant start() calls.
14-
*/
15-
class DevGlobeStartupActivity : StartupActivity.DumbAware {
9+
class DevGlobeStartupActivity : ProjectActivity {
1610

17-
override fun runActivity(project: Project) {
11+
override suspend fun execute(project: Project) {
1812
val apiKey = ApiKeyStorage.get() ?: return
1913
val settings = DevGlobeSettings.getInstance()
2014
val tracker = DevGlobeTracker.getInstance()
2115

22-
// start() is guarded by an AtomicBoolean — safe against concurrent project opens
2316
if (settings.state.trackingEnabled) {
2417
tracker.restoreConnected(apiKey)
2518
tracker.start(apiKey)

jetbrains-plugin/src/main/kotlin/xyz/devglobe/plugin/core/CoreClient.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,7 @@ class CoreClient(private val binaryPath: String) : Disposable {
165165
}
166166

167167
override fun dispose() {
168-
try {
169-
sendShutdown()
170-
process?.waitFor(2, java.util.concurrent.TimeUnit.SECONDS)
171-
} catch (_: Exception) { }
168+
try { sendShutdown() } catch (_: Exception) { }
172169
process?.destroyForcibly()
173170
process = null
174171
writer = null

jetbrains-plugin/src/main/kotlin/xyz/devglobe/plugin/core/DevGlobeTracker.kt

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,31 +36,36 @@ class DevGlobeTracker : Disposable {
3636
}
3737

3838
fun start(apiKey: String) {
39-
started.set(true)
39+
if (!started.compareAndSet(false, true)) return
4040
currentApiKey = apiKey
4141
val settings = DevGlobeSettings.getInstance()
4242

43-
ensureCore()
44-
coreClient?.sendInit(apiKey, settings.state.shareRepo, settings.state.anonymousMode, settings.state.statusMessage)
45-
coreClient?.sendResume()
46-
47-
registerDocumentListener()
43+
ApplicationManager.getApplication().executeOnPooledThread {
44+
ensureCore()
45+
coreClient?.sendInit(apiKey, settings.state.shareRepo, settings.state.anonymousMode, settings.state.statusMessage)
46+
coreClient?.sendResume()
47+
ApplicationManager.getApplication().invokeLater { registerDocumentListener() }
48+
}
4849
}
4950

51+
@Synchronized
5052
fun pause() {
53+
started.set(false)
5154
coreClient?.sendPause()
5255
state = state.copy(tracking = false)
5356
pushState()
5457
}
5558

59+
@Synchronized
5660
fun stop() {
5761
started.set(false)
58-
pause()
62+
coreClient?.sendPause()
63+
state = state.copy(tracking = false, connected = false)
5964
currentApiKey = null
60-
state = state.copy(connected = false)
6165
pushState()
6266
}
6367

68+
@Synchronized
6469
fun reset() {
6570
started.set(false)
6671
coreClient?.sendShutdown()
@@ -71,6 +76,7 @@ class DevGlobeTracker : Disposable {
7176
pushState()
7277
}
7378

79+
@Synchronized
7480
fun restoreConnected(apiKey: String) {
7581
currentApiKey = apiKey
7682
val settings = DevGlobeSettings.getInstance()
@@ -84,16 +90,20 @@ class DevGlobeTracker : Disposable {
8490
pushState()
8591
}
8692

93+
@Synchronized
8794
fun setStatusMessage(message: String) {
8895
state = state.copy(statusMessage = message)
8996
pushState()
9097
}
9198

9299
fun sendSetStatus(message: String) {
93-
ensureCore()
94-
coreClient?.sendSetStatus(message)
100+
ApplicationManager.getApplication().executeOnPooledThread {
101+
ensureCore()
102+
coreClient?.sendSetStatus(message)
103+
}
95104
}
96105

106+
@Synchronized
97107
fun updatePreference(key: String, value: Boolean) {
98108
state = when (key) {
99109
"shareRepo" -> state.copy(shareRepo = value)
@@ -108,6 +118,7 @@ class DevGlobeTracker : Disposable {
108118
}
109119
}
110120

121+
@Synchronized
111122
private fun ensureCore() {
112123
if (coreClient != null) return
113124

@@ -125,7 +136,7 @@ class DevGlobeTracker : Disposable {
125136

126137
val client = CoreClient(CoreDownloader.getBinaryPath())
127138
client.onState = { newState ->
128-
state = newState
139+
synchronized(this) { state = newState }
129140
pushState()
130141
}
131142
client.onOffline = { msg ->

0 commit comments

Comments
 (0)