mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-11-04 10:13:59 +00:00
fix(ios): run Zipline on an 8 MiB stack thread
newSingleThreadContext threads get only 512 KiB of stack on iOS, while QuickJS may recurse ~6 MiB deep when compiling a plugin, so the app crashed with SIGBUS at startup once a plugin was installed. Host the dispatcher on an NSThread with an 8 MiB stack, matching JVM/Android.
This commit is contained in:
parent
ff7a1714ff
commit
7eb56c8593
@ -40,7 +40,8 @@ class ZiplineDispatcher(
|
||||
* size is ~1 MiB, which causes native stack overflow when compiling large
|
||||
* JS modules like kotlin-stdlib (~491 KB).
|
||||
*
|
||||
* This expect/actual ensures the backing thread has a sufficiently large stack.
|
||||
* iOS secondary threads get only 512 KiB, so every platform must create its own
|
||||
* large-stack thread. This expect/actual ensures the backing thread has one.
|
||||
*/
|
||||
expect fun createZiplineDispatcher(): ZiplineDispatcher
|
||||
|
||||
|
||||
@ -17,17 +17,32 @@
|
||||
|
||||
package org.spotube.spotube.core.zipline
|
||||
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import platform.Foundation.NSThread
|
||||
import kotlin.coroutines.ContinuationInterceptor
|
||||
|
||||
// Same as JVM/Android. Zipline lets QuickJS recurse up to ~6 MiB of native stack, but
|
||||
// iOS gives secondary threads (incl. newSingleThreadContext's) only 512 KiB, so loading
|
||||
// a plugin overflowed into the guard page and crashed the app with SIGBUS.
|
||||
private const val ZIPLINE_THREAD_STACK_SIZE: ULong = 8_388_608u // 8 MiB
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
|
||||
actual fun createZiplineDispatcher(): ZiplineDispatcher {
|
||||
// On iOS/native, thread stack sizes are typically 8 MiB by default,
|
||||
// so newSingleThreadContext is sufficient.
|
||||
val ctx = newSingleThreadContext("Zipline")
|
||||
return ZiplineDispatcher(ctx) {
|
||||
ctx.close()
|
||||
val dispatcher = CompletableDeferred<CoroutineDispatcher>()
|
||||
val stopped = CompletableDeferred<Unit>()
|
||||
// NSThread is the only way to choose a thread's stack size here; it hosts an event
|
||||
// loop (runBlocking) whose dispatcher accepts work from any thread.
|
||||
val thread = NSThread {
|
||||
runBlocking {
|
||||
dispatcher.complete(coroutineContext[ContinuationInterceptor] as CoroutineDispatcher)
|
||||
stopped.await()
|
||||
}
|
||||
}
|
||||
thread.name = "Zipline"
|
||||
thread.stackSize = ZIPLINE_THREAD_STACK_SIZE
|
||||
thread.start()
|
||||
return ZiplineDispatcher(runBlocking { dispatcher.await() }) {
|
||||
stopped.complete(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user