Skip to content

fix: release peer lock before persistence #99

Description

@ovitrif

Summary

Android production ANRs in synonymdev/bitkit-android#1036 show Node::list_peers blocked while acquiring PeerStore.peers.read().

PeerStore::add_peer and PeerStore::remove_peer currently retain peers.write() while persisting the updated peer set. With VssStore, the synchronous KVStoreSync::write bridges remote persistence through block_in_place and internal_runtime.block_on. A slow remote write therefore retains the peer-store write lock for network duration and blocks every synchronous list_peers reader.

Symbolicated production stack

The affected Bitkit v2.3.0 build 182 uses ldk-node-android:0.7.0-rc.46 from source commit 3ace915250296cbbc3069b2c23030d2cdf7ba834.

The released Bitkit APK, rc.46 Maven AAR, and committed rc.46 Android library contain the same arm64 ELF:

  • size: 14,213,792 bytes
  • SHA-256: fbb450b145054f0682d966c5326cf88a477c9efd1c82b3c74e70d0bb7acabc48
  • .text: 0x561280..0xd338d0

The Play program counters are direct ELF VMAs. The exported uniffi_ldk_node_fn_method_node_list_peers symbol starts at 0x963f88; Play frame 0x963fc4 is +60 inside that function.

#00 bionic syscall
#01 0xcfa090 std futex wait / RwLock read contention
#02 0x661c5c std::sys::sync::rwlock::futex::RwLock::read_contended
#03 0x791a90 ldk_node::peer_store::PeerStore::list_peers
#04 0x79ba58 ldk_node::Node::list_peers
#05 0x60cfe8 UniFFI scaffolding for Node::list_peers
#06 0x619894 UniFFI callback wrapper for Node::list_peers
#07 0x963fc4 uniffi_ldk_node_fn_method_node_list_peers + 60

Source attribution:

  • src/peer_store.rs:44-72: add_peer and remove_peer retain self.peers.write() across persist_peers
  • src/peer_store.rs:74-75: list_peers acquires self.peers.read()
  • src/peer_store.rs:82-102: persist_peers calls KVStoreSync::write
  • src/io/vss_store.rs:191-219: VssStore::write waits for remote persistence through block_in_place and internal_runtime.block_on

The same lock scope remains on current fork main at a94255c88dff1c723fc1741dc52a5b4f664cb95f.

Required behavior

  • Serialize peer mutations independently from peer snapshot reads.
  • Restrict the peer RwLock to the in-memory mutation and snapshot.
  • Complete storage I/O after releasing the peer RwLock.
  • Preserve persistence failure and retry semantics.
  • Add a concurrent regression test whose fake KVStoreSync::write remains blocked while list_peers returns promptly.
  • Bump the crate version, regenerate mobile bindings, and publish a release for Android and iOS consumption.

Upstream lightningdevkit#919 implemented this lock-scope pattern in commit d49b681.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions