Application-level timeout/backoff handling is always scary to me, because I don't know how to make robust tests for it. I wonder if you couldn't use the same I/O-less approach, and split the logic out into pure functions that take the time passed/error state/... as value arguments, instead of measuring the physical time using OS APIs. It's probably not something for reusable libraries, but it could still be a nice benefit to be able to unit test in detail.