also looking at this
fix(ios): report async pool exhaustion as busy
#267
19 files
+53
-65
| @@ -374,16 +374,14 @@ pub const ContactPickerBridge = struct { | ||
| 374 | 374 | } |
| 375 | 375 | }; |
| 376 | 376 | |
| 377 | /// The answer for a full block pool, copied from `bridge_mobile_location`: | |
| 378 | /// `BridgeError` has no "Busy", `INVALID_PARAMETER` is the migration notes' | |
| 379 | /// designated stand-in, and the point is that the caller gets an explicit | |
| 380 | /// rejection instead of a promise that never settles. | |
| 377 | /// The answer for a full block pool. The call is valid, but all reply capacity | |
| 378 | /// is temporarily leased, so the page can retry rather than blaming its input. | |
| 381 | 379 | fn poolFull() bridge_error.BridgeError { |
| 382 | 380 | std.log.warn( |
| 383 | 381 | "pickContact refused: all {d} async slots are in flight", |
| 384 | 382 | .{ios_async.max_in_flight}, |
| 385 | 383 | ); |
| 386 | return bridge_error.BridgeError.InvalidParameter; | |
| 384 | return bridge_error.BridgeError.Busy; | |
| 387 | 385 | } |
| 388 | 386 | |
| 389 | 387 | /// The answer for a second `pickContact` while one is on screen. |
| @@ -395,16 +393,15 @@ fn poolFull() bridge_error.BridgeError { | ||
| 395 | 393 | /// promise alive. Swift does neither: it overwrites `pendingCallbackId` and the |
| 396 | 394 | /// first caller waits forever on an untimed promise. |
| 397 | 395 | /// |
| 398 | /// Same `INVALID_PARAMETER` stand-in as `poolFull`, for the same reason — | |
| 399 | /// `BridgeError` has no word for "busy" — with a log line that says which of | |
| 400 | /// the two happened. | |
| 396 | /// Same retryable `Busy` answer as `poolFull`, with a log line that says which | |
| 397 | /// kind of capacity is occupied. | |
| 401 | 398 | fn alreadyPresented() bridge_error.BridgeError { |
| 402 | 399 | std.log.warn( |
| 403 | 400 | "pickContact refused: a contact picker is already presented and still waiting for an " ++ |
| 404 | 401 | "answer; the first call is left alone rather than being stranded", |
| 405 | 402 | .{}, |
| 406 | 403 | ); |
| 407 | return bridge_error.BridgeError.InvalidParameter; | |
| 404 | return bridge_error.BridgeError.Busy; | |
| 408 | 405 | } |
| 409 | 406 | |
| 410 | 407 | // ============================================================================= |
| @@ -1617,6 +1614,7 @@ test "a second request while one is presented is refused, and the first is left | ||
| 1617 | 1614 | // promise never settles; the picker on screen is about to answer, so the |
| 1618 | 1615 | // newcomer is the one that has to be told no. |
| 1619 | 1616 | try testing.expect(!publishPending(.{ .ticket = fakeTicket(4, 9), .sels = fakeSels() })); |
| 1617 | try testing.expectEqual(bridge_error.BridgeError.Busy, alreadyPresented()); | |
| 1620 | 1618 | |
| 1621 | 1619 | // And the slot still holds the *first* ticket, not the newcomer's — a |
| 1622 | 1620 | // refusal that quietly replaced would look identical from the return value. |
| @@ -1626,6 +1624,10 @@ test "a second request while one is presented is refused, and the first is left | ||
| 1626 | 1624 | try testing.expectEqual(first.generation, pending.?.ticket.generation); |
| 1627 | 1625 | } |
| 1628 | 1626 | |
| 1627 | test "a full async pool reports that the call is retryable" { | |
| 1628 | try testing.expectEqual(bridge_error.BridgeError.Busy, poolFull()); | |
| 1629 | } | |
| 1630 | ||
| 1629 | 1631 | test "the slot is handed over once, so a double-firing delegate cannot reply twice" { |
| 1630 | 1632 | resetPendingForTesting(); |
| 1631 | 1633 | defer resetPendingForTesting(); |