Skip to content

Commit bf5cd6a

Browse files
authored
Fix IDE deadlock when logging into Builder ID / SSO (#3685)
* Caused by connection switch lock on pooled thread waiting for dialog pinning prompt (on EDT), while status bar widget update waiting for lock release on EDT * Causes subsequent warning on unsafe actions holding write lock, fix by setting modality state appropriately * No tests because unit tests have a different thread model than a running IDE
1 parent f79fda8 commit bf5cd6a

File tree

3 files changed

+34
-32
lines changed

3 files changed

+34
-32
lines changed

jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/DefaultToolkitConnectionManager.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,35 +97,34 @@ class DefaultToolkitConnectionManager : ToolkitConnectionManager, PersistentStat
9797
}
9898

9999
@Synchronized
100-
override fun switchConnection(connection: ToolkitConnection?) {
100+
override fun switchConnection(newConnection: ToolkitConnection?) {
101101
val oldConnection = this.connection
102-
val newConnection = connection
103102

104103
if (oldConnection != newConnection) {
104+
val application = ApplicationManager.getApplication()
105105
this.connection = newConnection
106106

107-
val pinningManager = pinningManager
108-
if (newConnection != null && pinningManager != null) {
107+
if (newConnection != null) {
109108
val featuresToPin = mutableListOf<FeatureWithPinnedConnection>()
110109
FeatureWithPinnedConnection.EP_NAME.forEachExtensionSafe {
111110
if (!pinningManager.isFeaturePinned(it) &&
112-
113111
(
114112
(oldConnection == null && it.supportsConnectionType(newConnection)) ||
115113
(oldConnection != null && it.supportsConnectionType(oldConnection) != it.supportsConnectionType(newConnection))
116114
)
117-
118115
) {
119116
featuresToPin.add(it)
120117
}
121118
}
122119

123120
if (featuresToPin.isNotEmpty()) {
124-
pinningManager.maybePinFeatures(oldConnection, newConnection, featuresToPin)
121+
application.executeOnPooledThread {
122+
pinningManager.maybePinFeatures(oldConnection, newConnection, featuresToPin)
123+
}
125124
}
126125
}
127126

128-
ApplicationManager.getApplication().messageBus.syncPublisher(ToolkitConnectionManagerListener.TOPIC).activeConnectionChanged(connection)
127+
application.messageBus.syncPublisher(ToolkitConnectionManagerListener.TOPIC).activeConnectionChanged(newConnection)
129128
}
130129
}
131130
}

jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/ToolkitAuthManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ interface ToolkitConnectionManager : Disposable {
9090

9191
fun activeConnectionForFeature(feature: FeatureWithPinnedConnection): ToolkitConnection?
9292

93-
fun switchConnection(connection: ToolkitConnection?)
93+
fun switchConnection(newConnection: ToolkitConnection?)
9494

9595
override fun dispose() {}
9696

jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/pinning/ConnectionPinningManager.kt

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.core.credentials.pinning
66
import com.intellij.icons.AllIcons
77
import com.intellij.openapi.Disposable
88
import com.intellij.openapi.application.ApplicationManager
9+
import com.intellij.openapi.application.ModalityState
910
import com.intellij.openapi.components.PersistentStateComponent
1011
import com.intellij.openapi.components.State
1112
import com.intellij.openapi.components.Storage
@@ -14,7 +15,7 @@ import com.intellij.openapi.extensions.ExtensionPointName
1415
import com.intellij.openapi.project.Project
1516
import com.intellij.openapi.ui.DialogWrapper
1617
import com.intellij.openapi.ui.MessageDialogBuilder
17-
import org.jetbrains.annotations.TestOnly
18+
import org.jetbrains.annotations.VisibleForTesting
1819
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
1920
import software.aws.toolkits.jetbrains.core.credentials.ToolkitAuthManager
2021
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
@@ -98,7 +99,7 @@ class DefaultConnectionPinningManager :
9899
}
99100

100101
var connectionToPin = if (oldConnection is AwsBearerTokenConnection) oldConnection else newConnection
101-
if (computeOnEdt { showDialogIfNeeded(oldConnection, newConnection, featuresString) }) {
102+
if (showDialogIfNeeded(oldConnection, newConnection, featuresString)) {
102103
features.forEach {
103104
setPinnedConnection(it, connectionToPin)
104105
}
@@ -126,33 +127,35 @@ class DefaultConnectionPinningManager :
126127

127128
override fun dispose() {}
128129

129-
@TestOnly
130+
@VisibleForTesting
130131
internal fun showDialogIfNeeded(oldConnection: ToolkitConnection?, newConnection: ToolkitConnection, featuresString: String, project: Project? = null) =
131132
if (!doNotPromptForPinning) {
132133
val bearerTokenConnectionName = bearerTokenConnectionString(oldConnection, newConnection)
133134

134-
MessageDialogBuilder.yesNo(
135-
message("credentials.switch.confirmation.title", featuresString, bearerTokenConnectionName),
136-
message("credentials.switch.confirmation.comment", featuresString, bearerTokenConnectionName, message("iam.name"),)
137-
)
138-
.yesText(message("credentials.switch.confirmation.yes"))
139-
.noText(message("credentials.switch.confirmation.no"))
140-
.doNotAsk(object : com.intellij.openapi.ui.DoNotAskOption.Adapter() {
141-
override fun rememberChoice(isSelected: Boolean, exitCode: Int) {
142-
if (isSelected && exitCode == DialogWrapper.OK_EXIT_CODE) {
143-
doNotPromptForPinning = true
135+
computeOnEdt(ModalityState.defaultModalityState()) {
136+
MessageDialogBuilder.yesNo(
137+
message("credentials.switch.confirmation.title", featuresString, bearerTokenConnectionName),
138+
message("credentials.switch.confirmation.comment", featuresString, bearerTokenConnectionName, message("iam.name"))
139+
)
140+
.yesText(message("credentials.switch.confirmation.yes"))
141+
.noText(message("credentials.switch.confirmation.no"))
142+
.doNotAsk(object : com.intellij.openapi.ui.DoNotAskOption.Adapter() {
143+
override fun rememberChoice(isSelected: Boolean, exitCode: Int) {
144+
if (isSelected && exitCode == DialogWrapper.OK_EXIT_CODE) {
145+
doNotPromptForPinning = true
146+
}
147+
}
148+
})
149+
.icon(AllIcons.General.QuestionDialog)
150+
.help(HelpIds.EXPLORER_CREDS_HELP.id)
151+
.ask(project).apply {
152+
if (this) {
153+
UiTelemetry.click(project, "connection_multiple_auths_yes")
154+
} else {
155+
UiTelemetry.click(project, "connection_multiple_auths_no")
144156
}
145157
}
146-
})
147-
.icon(AllIcons.General.QuestionDialog)
148-
.help(HelpIds.EXPLORER_CREDS_HELP.id)
149-
.ask(project).apply {
150-
if (this) {
151-
UiTelemetry.click(project, "connection_multiple_auths_yes")
152-
} else {
153-
UiTelemetry.click(project, "connection_multiple_auths_no")
154-
}
155-
}
158+
}
156159
} else {
157160
false
158161
}

0 commit comments

Comments
 (0)