Skip to content

Commit

Permalink
Fixup deadlock when hammering futures of a certain type
Browse files Browse the repository at this point in the history
  • Loading branch information
jhugman authored and Daniel Salinas committed Sep 5, 2024
1 parent c935961 commit 5ccee68
Showing 1 changed file with 25 additions and 2 deletions.
27 changes: 25 additions & 2 deletions typescript/src/async-rust-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ type PollFunc = (
handle: UniffiHandle,
) => void;

// Calls setTimeout and then resolves the promise.
// This may be used as a simple yield.
export async function delayPromise(delayMs: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, delayMs));
}

/**
* This method calls an asynchronous method on the Rust side.
*
Expand Down Expand Up @@ -65,13 +71,30 @@ export async function uniffiRustCallAsync<F, T>(
// The poll, complete and free methods are specialized by the FFIType of the return value.
try {
let pollResult: number | undefined;
do {
while (true) {
// Calling pollFunc with a callback that resolves the promise that pollRust
// returns: pollRust makes the promise, uniffiFutureContinuationCallback resolves it.
pollResult = await pollRust((handle) => {
pollFunc(rustFuture, uniffiFutureContinuationCallback, handle);
});
} while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY);
// From https://github.com/mozilla/uniffi-rs/pull/1837/files#diff-8a28c9cf1245b4f714d406ea4044d68e1000099928eaca1afb504ccbc008fe9fR35-R37
//
// > WARNING: the call to [rust_future_poll] must be scheduled to happen soon after the callback is
// > called, but not inside the callback itself. If [rust_future_poll] is called inside the
// > callback, some futures will deadlock and our scheduler code might as well.
//
// This delay is to ensure that `uniffiFutureContinuationCallback` returns before the next poll, i.e.
// so that the next poll is outside of this callback.
//
// The length of the delay seems to be significant (at least in tests which hammer a network).
// I would like to understand this more: I am still seeing deadlocks when this drops below its current
// delay, but these maybe related to a different issue, as alluded to in
// https://github.com/mozilla/uniffi-rs/pull/1901
if (pollResult == UNIFFI_RUST_FUTURE_POLL_READY) {
break;
}
await delayPromise(100);
}

// Now it's ready, all we need to do is pick up the result (and error).
return liftFunc(
Expand Down

0 comments on commit 5ccee68

Please sign in to comment.