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
|
* size is ~1 MiB, which causes native stack overflow when compiling large
|
||||||
* JS modules like kotlin-stdlib (~491 KB).
|
* 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
|
expect fun createZiplineDispatcher(): ZiplineDispatcher
|
||||||
|
|
||||||
|
|||||||
@ -17,17 +17,32 @@
|
|||||||
|
|
||||||
package org.spotube.spotube.core.zipline
|
package org.spotube.spotube.core.zipline
|
||||||
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.newSingleThreadContext
|
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 {
|
actual fun createZiplineDispatcher(): ZiplineDispatcher {
|
||||||
// On iOS/native, thread stack sizes are typically 8 MiB by default,
|
val dispatcher = CompletableDeferred<CoroutineDispatcher>()
|
||||||
// so newSingleThreadContext is sufficient.
|
val stopped = CompletableDeferred<Unit>()
|
||||||
val ctx = newSingleThreadContext("Zipline")
|
// NSThread is the only way to choose a thread's stack size here; it hosts an event
|
||||||
return ZiplineDispatcher(ctx) {
|
// loop (runBlocking) whose dispatcher accepts work from any thread.
|
||||||
ctx.close()
|
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