Description
On our armv7 (32-bit) embedded Linux board we're using Swift 6.0 and the AsyncHTTPClient
. For a while we noticed that performing retries with the HTTPClient
would crash about 26-27 total retries with the following error:
Swift/Integers.swift:1610: Fatal error: Distance is not representable in Int
Current stack trace:
0 libswiftCore.so 0x00000000b6dbf008 _swift_stdlib_reportFatalErrorInFile + 108
1 libswiftCore.so 0x00000000b69995d8 <unavailable> + 1431000
2 libswiftCore.so 0x00000000b699ad2c <unavailable> + 1436972
3 libswiftCore.so 0x00000000b6b7c44c _assertionFailure(_:_:file:line:flags:) + 228
4 libswiftCore.so 0x00000000b6af42fc <unavailable> + 2851580
5 libswiftCore.so 0x00000000b6babcd8 ClosedRange<>.distance(from:to:) + 1032
6 libswiftCore.so 0x00000000b69b915c <unavailable> + 1560924
7 libswiftCore.so 0x00000000b6b6b684 Collection.count.getter + 184
8 libswiftCore.so 0x00000000b69b0a84 <unavailable> + 1526404
9 libswiftCore.so 0x00000000b6b9b990 Collection.randomElement() + 20
10 calculate-backoff-32bit 0x000000007fdb09ac <unavailable> + 8403372
11 calculate-backoff-32bit 0x000000007fdafc38 <unavailable> + 8399928
12 libc.so.6 0x00000000b6385e4d __libc_start_main + 149
I set out to figure out "why" with gdb and backtraces and found that the HTTPConnectionPool.calculateBackoff
is the main culprit, and crashes on the use of .randomElement
:
let jitter: TimeAmount = .nanoseconds((-jitterRange...jitterRange).randomElement()!)
After further investigation into the Swift stdlib as to why it crashes, it seems that the randomElement
method is implemented using Int.random:
public func randomElement<T: RandomNumberGenerator>(
using generator: inout T
) -> Element? {
guard !isEmpty else { return nil }
let random = Int.random(in: 0 ..< count, using: &generator)
let idx = index(startIndex, offsetBy: random)
return self[idx]
}
On 32-bit systems, this means that if you're trying to get a randomElement on a range that is larger than what fits in Int32
(same as Int
), then the code will crash. This seems like maybe a problem or oversight, that can be reported separately to swiftlang/swift.
However, there is a workaround that works just as well and avoids this issue. May I suggest changing this .randomElement to using Int64.random
instead?
let jitter: TimeAmount = .nanoseconds(Int64.random(in: -jitterRange...jitterRange))
This theoretically should provide the same result as .randomElement, and works correctly on 32-bit systems.