Skip to content

Commit 894bcab

Browse files
committed
fix: URI handling race condition when creating polling jobs
- when opening a URI, multiple polling jobs could be triggered on different Coder deployments if Toolbox starts from scratch. This happens because Toolbox takes longer to complete its initial plugin initialization, while the URI handling logic runs faster and doesn't wait properly for the plugin to be ready, leading to an early polling job. Meanwhile, once Toolbox finishes its initialization, it also triggers another polling job. - this patch properly waits for the plugin initialization and properly cancel the initial polling job, which is then replaced by the URI handling polling job.
1 parent 0b49bcb commit 894bcab

File tree

2 files changed

+16
-9
lines changed

2 files changed

+16
-9
lines changed

src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt

+7-2
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ class CoderRemoteProvider(
122122
environments.update {
123123
LoadableState.Value(resolvedEnvironments.toList())
124124
}
125+
if (isInitialized.value == false) {
126+
context.logger.info("Environments for ${client.url} are now initialized")
127+
isInitialized.update {
128+
true
129+
}
130+
}
125131

126132
lastEnvironments = resolvedEnvironments
127133
} catch (_: CancellationException) {
@@ -238,7 +244,7 @@ class CoderRemoteProvider(
238244
// start initialization with the new settings
239245
this@CoderRemoteProvider.client = restClient
240246
coderHeaderPager = NewEnvironmentPage(context, context.i18n.pnotr(restClient.url.toString()))
241-
poll(restClient, cli)
247+
pollJob = poll(restClient, cli)
242248
}
243249
}
244250

@@ -324,7 +330,6 @@ class CoderRemoteProvider(
324330
pollJob?.cancel()
325331
pollJob = poll(client, cli)
326332
goToEnvironmentsPage()
327-
isInitialized.update { true }
328333
}
329334

330335
/**

src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt

+9-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import com.coder.toolbox.settings.CoderSettings
1313
import kotlinx.coroutines.TimeoutCancellationException
1414
import kotlinx.coroutines.delay
1515
import kotlinx.coroutines.flow.StateFlow
16-
import kotlinx.coroutines.flow.filter
16+
import kotlinx.coroutines.flow.first
1717
import kotlinx.coroutines.launch
1818
import kotlinx.coroutines.time.withTimeout
1919
import kotlinx.coroutines.yield
@@ -41,7 +41,7 @@ open class CoderProtocolHandler(
4141
*/
4242
suspend fun handle(
4343
uri: URI,
44-
onReinitialize: suspend (CoderRestClient, CoderCLIManager) -> Unit
44+
reInitialize: suspend (CoderRestClient, CoderCLIManager) -> Unit
4545
) {
4646
val params = uri.toQueryParameters()
4747

@@ -148,11 +148,12 @@ open class CoderProtocolHandler(
148148
context.logger.info("Configuring Coder CLI...")
149149
cli.configSsh(restClient.agentNames(workspaces))
150150

151-
onReinitialize(restClient, cli)
151+
isInitialized.waitForTrue()
152+
reInitialize(restClient, cli)
153+
152154
context.cs.launch {
153155
context.ui.showWindow()
154156
context.envPageManager.showPluginEnvironmentsPage(true)
155-
isInitialized.waitForTrue()
156157
context.envPageManager.showEnvironmentPage("${workspace.name}.${agent.name}", false)
157158
// without a yield or a delay(0) the env page does not show up. My assumption is that
158159
// the coroutine is finishing too fast without giving enough time to compose main thread
@@ -366,8 +367,9 @@ internal fun getMatchingAgent(
366367
return agent
367368
}
368369

369-
fun StateFlow<Boolean>.waitForTrue() {
370-
this.filter { it }
371-
}
370+
/**
371+
* Suspends the coroutine until first true value is received.
372+
*/
373+
suspend fun StateFlow<Boolean>.waitForTrue() = this.first { it }
372374

373375
class MissingArgumentException(message: String, ex: Throwable? = null) : IllegalArgumentException(message, ex)

0 commit comments

Comments
 (0)