Skip to content

A few concurrency-related issues Block has faced while using bdk-ffi #205

@cgarrett-square

Description

@cgarrett-square

At Block, We're using the BDK via the bdk-ffi bindings in our iOS and Android Bitcoin Wallet. It's been incredibly helpful so far! We surfaced a few issues related to synchronicity in the BDK #mobile Discord channel and are creating this issue to track them. As discussed in the thread, I'm starting with a single issue with multiple related topics. But I'm happy to break this into three separate GitHub issues if the team prefers! Also, I'm acknowledging I'm not using the issue template until we've decided if this should be a single issue or three. But I'm happy to provide the additional detail requested in the template when helpful.

Issue 1: Concurrent Calls to Wallet in BDK

If we make calls concurrently to the BDK wallet, we sometimes experience the main thread hanging on a mutex lock. The calls involved in one example were sync and get_balance. Below is a sample stack trace from an app hang when we were calling into the BDK from multiple threads: get_balance on Thread 1 (main) and sync on Thread 6 (background):

block-mutex-wait

We changed our implementation to ensure all calls made to the BDK wallet are serialized, which fixes the issue. But we worry about performance implications with this solution. We call sync often, and have also observed that call hanging and not returning (separate issue below). So, if our calls are forced to be serialized, we could end up in a situation where we have to wait on a call to sync (that may never return) to finish before executing some other work.

Issue 2: Long-running / Non-cancellable Calls

In our development, we have encountered calls to sync that never terminated or returned. Examining the long-running process usually shows work within the BDK related to Electrum. There is not a way to cancel an in-flight call in BDK, which causes issues when / if this happens. On Android we tried to wrap blocking BDK calls into runInterruptible to make them cancellable, which seems to do the trick. We additionally tried to wrap the calls with withTimeout to prevent them from hanging for too long but that doesn’t work. We also have concerns about resources/memory leaks when wrapping blocking calls into cancellable coroutines without being able to properly clean up those calls.

// TODO: Timeout doesn't seem to be able to properly cancel the coroutine with blocking BDK call.
withTimeout(timeout = 5.seconds) {
  // Allows us to cancel these calls by killing coroutine scope.
  // TODO: This potentially can cause memory/resource leaks. Ideally BDK calls would be wrapped into Closeable.
  runInterruptible {
    bdkWallet.sync(
      blockchain = blockchain,
      progress = null,
    )
  }
}

Issue 3: Synchronous API

All methods in the BDK API are synchronous, but many are expensive and often take between hundreds of milliseconds to multiple seconds. We are currently manually wrapping the BDK API with our own asynchronous API. It would be nice if the BDK API could adopt an async or callback model to reduce the overhead of using it.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions