refactor: confine all Zipline parser service ops to Zipline thread

This commit is contained in:
Harsh Shandilya
2026-06-08 17:12:56 +05:30
parent 4539e0a6b9
commit 13a9d72c35
@@ -18,11 +18,14 @@ import dev.msfjarvis.claw.parser.LobstersParserService
import dev.msfjarvis.claw.parser.model.ParserSerializersModule
import java.io.File
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicReference
import kotlin.time.Duration.Companion.days
import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okio.ByteString.Companion.decodeHex
import okio.FileSystem
@@ -34,9 +37,18 @@ class AndroidZiplineParserClient(
private val httpClient: OkHttpClient,
private val verifySignatures: Boolean,
) : LobstersParserClient, AutoCloseable {
private val ziplineThread = AtomicReference<Thread>()
private val dispatcher: ExecutorCoroutineDispatcher =
Executors.newSingleThreadExecutor { runnable ->
Thread(null, runnable, "Claw-ZiplineParser", ZIPLINE_THREAD_STACK_SIZE_BYTES)
Thread(
null,
{
ziplineThread.set(Thread.currentThread())
runnable.run()
},
"Claw-ZiplineParser",
ZIPLINE_THREAD_STACK_SIZE_BYTES,
)
}
.asCoroutineDispatcher()
private val mutex = Mutex()
@@ -53,7 +65,7 @@ class AndroidZiplineParserClient(
override fun close() {
loadedService?.close()
loadedService = null
loadedZipline?.close()
loadedZipline?.let { zipline -> onZiplineThread { zipline.close() } }
loadedZipline = null
dispatcher.close()
}
@@ -95,23 +107,59 @@ class AndroidZiplineParserClient(
manifestUrl
}
return when (
val result =
loader.loadOnce(
applicationName = "zipline-parser",
freshnessChecker = freshnessChecker,
manifestUrl = effectiveManifestUrl,
serializersModule = ParserSerializersModule,
)
) {
is LoadResult.Success -> {
loadedZipline = result.zipline
result.zipline.take("LobstersParserService")
return withContext(dispatcher) {
when (
val result =
loader.loadOnce(
applicationName = "zipline-parser",
freshnessChecker = freshnessChecker,
manifestUrl = effectiveManifestUrl,
serializersModule = ParserSerializersModule,
)
) {
is LoadResult.Success -> {
loadedZipline = result.zipline
DispatcherConfinedLobstersParserService(
result.zipline.take("LobstersParserService")
)
}
is LoadResult.Failure -> throw result.exception
}
is LoadResult.Failure -> throw result.exception
}
}
internal fun <T> onZiplineThread(block: () -> T): T {
return if (Thread.currentThread() === ziplineThread.get()) {
block()
} else {
runBlocking(dispatcher) { block() }
}
}
private inner class DispatcherConfinedLobstersParserService(
private val delegate: LobstersParserService
) : LobstersParserService {
override fun parsePostsPage(html: String) = onZiplineThread { delegate.parsePostsPage(html) }
override fun parsePostDetails(html: String) = onZiplineThread {
delegate.parsePostDetails(html)
}
override fun parseUser(html: String) = onZiplineThread { delegate.parseUser(html) }
override fun parseTagsPage(html: String) = onZiplineThread { delegate.parseTagsPage(html) }
override fun parseSearchResults(html: String) = onZiplineThread {
delegate.parseSearchResults(html)
}
override fun parseCsrfToken(html: String) = onZiplineThread { delegate.parseCsrfToken(html) }
override fun parseReplyForm(html: String) = onZiplineThread { delegate.parseReplyForm(html) }
override fun close() = onZiplineThread { delegate.close() }
}
private fun extractEmbeddedArtifacts() {
val assets = context.assets
val ziplineAssets = assets.list("zipline").orEmpty()