Production evidence
Bitkit Android production 2.4.1 / build 188 reproduced the owned-Tokio-runtime crash with ldk-node-android:0.7.0-rc.63 after the earlier Android teardown hardening:
- 3 events across 2 users in the first approximately 24 hours.
- Pixel 7 and Galaxy S24 on Android 16.
- Native Build ID
6698575d4a5da846e0532df23da0b799344053e7. - Worker-thread
SIGABRT with #00 abort and no Java exception. - Panic:
Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context. - Panic:
A Tokio 1.x context was found, but it is being shutdown.
The incident is tracked downstream in synonymdev/bitkit-android#982 and under crash epic synonymdev/bitkit-android#986. The first exact symbolication is documented in #94. PR #100 removed the Electrum client's strong runtime owner, but it did not make shutdown ownership and task destruction deterministic. The recurrence is on rc.63; rc.64 and rc.65 only change R8 consumer rules.
Root cause
The node-owned Tokio runtime was stored in a cloneable Arc<Runtime>. Background tasks, the event processor, exported child handles, and persistence or chain-sync paths could therefore outlive the node or become the runtime's final owner.
Shutdown did not close task admission and all update gates before signalling work. Timed-out tasks were aborted without fully draining their futures, and the event processor was not deterministically aborted and awaited. A runtime worker could consequently destroy the final runtime owner, or a chain/VSS future could continue polling after runtime shutdown began. Tokio panicked in either case, and the mobile release profile converted the panic to a process abort.
Resolution
PR #111 implements deterministic runtime ownership and teardown:
Node is the sole owner of the Tokio runtime.- Tasks and exported child handles use non-owning
RuntimeControl. - Shutdown closes task admission and the Electrum update gate before signalling work.
- Timed-out background tasks and the event processor are cancelled and drained before runtime disposal.
- Chain-source teardown waits until tracked chain work is destroyed.
- VSS I/O runs on an isolated store runtime with async-context-safe fallback disposal.
- Event-processor persistence failures are returned after cleanup completes.
- Public mobile APIs and wire formats remain unchanged.
- All package surfaces and regenerated Android, JVM, Python, and Swift artifacts are prepared as
0.7.0-rc.66.
Validation
cargo fmt --all -- --checkRUSTFLAGS='-D warnings' cargo buildcargo check --release --features unifficargo test --features uniffi --lib (107 passed)- Focused runtime, Electrum, VSS, and release-mode regressions
./bindgen.sh for JVM, Python, Swift, and all supported Android ABIs- Android AAR and native debug-symbol Build IDs match for every supported ABI; all native libraries are 16 KiB aligned.
- Android Maven Local validation: the signed Bitkit
devRelease build 188 completed 100 consecutive in-app LDK node restarts on an arm64-v8a Android 16/API 36 emulator without SIGABRT or Tokio runtime-shutdown failures.
Acceptance criteria
Status
PR #111 contains the implementation, 0.7.0-rc.66 version bump, and regenerated artifacts. The tag and release remain post-approval.
Production evidence
Bitkit Android production
2.4.1/ build188reproduced the owned-Tokio-runtime crash withldk-node-android:0.7.0-rc.63after the earlier Android teardown hardening:6698575d4a5da846e0532df23da0b799344053e7.SIGABRTwith#00 abortand no Java exception.Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context.A Tokio 1.x context was found, but it is being shutdown.The incident is tracked downstream in synonymdev/bitkit-android#982 and under crash epic synonymdev/bitkit-android#986. The first exact symbolication is documented in #94. PR #100 removed the Electrum client's strong runtime owner, but it did not make shutdown ownership and task destruction deterministic. The recurrence is on rc.63; rc.64 and rc.65 only change R8 consumer rules.
Root cause
The node-owned Tokio runtime was stored in a cloneable
Arc<Runtime>. Background tasks, the event processor, exported child handles, and persistence or chain-sync paths could therefore outlive the node or become the runtime's final owner.Shutdown did not close task admission and all update gates before signalling work. Timed-out tasks were aborted without fully draining their futures, and the event processor was not deterministically aborted and awaited. A runtime worker could consequently destroy the final runtime owner, or a chain/VSS future could continue polling after runtime shutdown began. Tokio panicked in either case, and the mobile release profile converted the panic to a process abort.
Resolution
PR #111 implements deterministic runtime ownership and teardown:
Nodeis the sole owner of the Tokio runtime.RuntimeControl.0.7.0-rc.66.Validation
cargo fmt --all -- --checkRUSTFLAGS='-D warnings' cargo buildcargo check --release --features unifficargo test --features uniffi --lib(107 passed)./bindgen.shfor JVM, Python, Swift, and all supported Android ABIsdevReleasebuild 188 completed 100 consecutive in-app LDK node restarts on an arm64-v8a Android 16/API 36 emulator without SIGABRT or Tokio runtime-shutdown failures.Acceptance criteria
stop()returns.Status
PR #111 contains the implementation,
0.7.0-rc.66version bump, and regenerated artifacts. The tag and release remain post-approval.