@@ -37,6 +37,7 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
3737 private const val BATCH_SIZE = 10
3838 private const val BATCH_DELAY = 100L
3939 private const val DEBOUNCE_DELAY = 500L
40+ private const val UI_UPDATE_BATCH_SIZE = 50 // Batch size for UI updates
4041 }
4142
4243 private val perClientPortForwardingManager = service<PerClientPortForwardingManager >()
@@ -92,7 +93,6 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
9293 val completableFuture = CompletableFuture <Void >()
9394
9495 val statusServiceStub = StatusServiceGrpc .newStub(GitpodManager .supervisorChannel)
95-
9696 val portsStatusRequest = Status .PortsStatusRequest .newBuilder().setObserve(true ).build()
9797
9898 val portsStatusResponseObserver = object :
@@ -104,8 +104,8 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
104104 override fun onNext (response : Status .PortsStatusResponse ) {
105105 debounceJob?.cancel()
106106 debounceJob = runJob(lifetime) {
107- delay(DEBOUNCE_DELAY )
108107 try {
108+ delay(DEBOUNCE_DELAY )
109109 syncPortsListWithClient(response)
110110 } catch (e: Exception ) {
111111 thisLogger().error(" gitpod: Error during port observation" , e)
@@ -125,15 +125,14 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
125125 }
126126
127127 statusServiceStub.portsStatus(portsStatusRequest, portsStatusResponseObserver)
128-
129128 return completableFuture
130129 }
131130
132131 private fun isLocalPortForwardingDisabled (): Boolean {
133132 return System .getenv(" GITPOD_DISABLE_JETBRAINS_LOCAL_PORT_FORWARDING" )?.toBoolean() ? : false
134133 }
135134
136- private fun syncPortsListWithClient (response : Status .PortsStatusResponse ) {
135+ private suspend fun syncPortsListWithClient (response : Status .PortsStatusResponse ) {
137136 val ignoredPorts = ignoredPortsForNotificationService.getIgnoredPorts()
138137 val portsList = response.portsList.filter { ! ignoredPorts.contains(it.localPort) }
139138 val portsNumbersFromPortsList = portsList.map { it.localPort }
@@ -156,57 +155,60 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService
156155 .map { it.hostPortNumber }
157156 .filter { portsNumbersFromNonServedPorts.contains(it) || ! portsNumbersFromPortsList.contains(it) }
158157
159- runJob(lifetime) {
160- coroutineScope {
161- // Stop operations first to free up resources
162- launch {
163- processPortsInBatches(forwardedPortsToStopForwarding) { port ->
164- operationSemaphore.withPermit { stopForwarding(port) }
165- }
158+ coroutineScope {
159+ // Stop operations first to free up resources
160+ launch {
161+ processPortsInBatches(forwardedPortsToStopForwarding) { port ->
162+ operationSemaphore.withPermit { stopForwarding(port) }
166163 }
167- launch {
168- processPortsInBatches(exposedPortsToStopExposingOnClient) { port ->
169- operationSemaphore.withPermit { stopExposingOnClient( port) }
170- }
164+ }
165+ launch {
166+ processPortsInBatches(exposedPortsToStopExposingOnClient) { port ->
167+ operationSemaphore.withPermit { stopExposingOnClient(port) }
171168 }
169+ }
172170
173- // Wait for stop operations to complete
174- awaitAll()
171+ // Wait for stop operations to complete
172+ awaitAll()
175173
176- // Start new operations
177- launch {
178- processPortsInBatches(servedPortsToStartForwarding) { port ->
179- operationSemaphore.withPermit {
180- startForwarding(port)
181- allPortsToKeep.add(port.localPort)
182- }
174+ // Start new operations
175+ launch {
176+ processPortsInBatches(servedPortsToStartForwarding) { port ->
177+ operationSemaphore.withPermit {
178+ startForwarding(port)
179+ allPortsToKeep.add(port.localPort)
183180 }
184181 }
185- launch {
186- processPortsInBatches(exposedPortsToStartExposingOnClient) { port ->
187- operationSemaphore.withPermit {
188- startExposingOnClient(port)
189- allPortsToKeep.add (port.localPort )
190- }
182+ }
183+ launch {
184+ processPortsInBatches(exposedPortsToStartExposingOnClient) { port ->
185+ operationSemaphore.withPermit {
186+ startExposingOnClient (port)
187+ allPortsToKeep.add(port.localPort)
191188 }
192189 }
190+ }
193191
194- // Update presentation in parallel with start operations
195- launch {
196- processPortsInBatches(portsList) { port ->
197- application.invokeLater {
192+ // Wait for all operations to complete
193+ awaitAll()
194+
195+ // Update presentation in batches to avoid UI thread blocking
196+ launch {
197+ portsList.chunked(UI_UPDATE_BATCH_SIZE ).forEach { batch ->
198+ application.invokeLater {
199+ batch.forEach { port ->
198200 updatePortsPresentation(port)
199201 allPortsToKeep.add(port.localPort)
200202 }
201203 }
204+ delay(50 ) // Small delay between UI updates to prevent overwhelming the EDT
202205 }
206+ }
203207
204- // Wait for all operations to complete
205- awaitAll()
208+ awaitAll()
206209
207- // Clean up after all operations are done
208- cleanupUnusedLifetimes(allPortsToKeep)
209- }
210+ // Clean up after all operations are done
211+ cleanupUnusedLifetimes(allPortsToKeep)
210212 }
211213 }
212214
0 commit comments