Merge pull request #3818 from vitorpamplona/claude/local-blossom-main-thread-anr-sfcfv9
Confine Blossom cache probe and resolver to IO dispatcher
This commit is contained in:
@@ -1230,6 +1230,14 @@ class AppModules(
|
||||
blossomResolver.uriToUrlCache.evictAll()
|
||||
blossomResolver.blossomHitCache.cache.evictAll()
|
||||
localBlossomCacheProbe.invalidate()
|
||||
// Re-probe immediately so enabling the feature activates it
|
||||
// this session. Otherwise `available` only advances when a
|
||||
// `blossom:` URI is resolved, and the common feed-image path
|
||||
// never routes through the resolver until `available` is
|
||||
// already true — so a freshly-enabled toggle (or a cache that
|
||||
// came up after launch) would stay dormant and the settings
|
||||
// "detected" chip would read stale.
|
||||
localBlossomCacheProbe.isAvailable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -29,11 +29,14 @@ import com.vitorpamplona.quartz.nip01Core.core.isValid
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomUri
|
||||
import com.vitorpamplona.quartz.utils.firstNotNullOrNullAsync
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@@ -57,10 +60,17 @@ class BlossomServerResolver(
|
||||
suspend fun findServers(uriStr: String): BlossomUriServer? {
|
||||
uriToUrlCache[uriStr]?.let { return it }
|
||||
|
||||
// Confined to Dispatchers.IO: this is reached from Compose
|
||||
// `produceState`/`LaunchedEffect` (RichTextViewer, MarmotGroupIconDisplay),
|
||||
// which run on the main dispatcher. The pre-suspension work here —
|
||||
// BlossomUri parsing, LruCache lookups, the local-cache probe's client
|
||||
// build, and the server-list flow setup — must stay off the UI thread.
|
||||
val result =
|
||||
withContext(Dispatchers.IO) {
|
||||
withTimeoutOrNull(10000) {
|
||||
findServersInner(uriStr)
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
uriToUrlCache.put(uriStr, result)
|
||||
|
||||
+9
@@ -22,10 +22,13 @@ package com.vitorpamplona.amethyst.service.uploads.blossom.bud10
|
||||
|
||||
import com.vitorpamplona.amethyst.model.privacyOptions.IRoleBasedHttpClientBuilder
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Request
|
||||
import okhttp3.coroutines.executeAsync
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -77,7 +80,12 @@ class LocalBlossomCacheProbe(
|
||||
cachedAtMs = 0L
|
||||
}
|
||||
|
||||
// Confined to Dispatchers.IO because callers reach this through suspend
|
||||
// resolvers invoked from Compose `LaunchedEffect`/`produceState`, which run
|
||||
// on the main dispatcher: building the OkHttp client and issuing the HEAD
|
||||
// must not touch the UI thread.
|
||||
private suspend fun probe(): Boolean =
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val baseClient = httpClientBuilder.okHttpClientForPreview(LOCAL_CACHE_BASE)
|
||||
val client =
|
||||
@@ -105,6 +113,7 @@ class LocalBlossomCacheProbe(
|
||||
if (e is CancellationException) throw e
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun currentTimeMs(): Long = System.currentTimeMillis()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user