@@ -912,8 +912,9 @@ enum { CBM_SPAWN_RETRY = 2, CBM_SPAWN_RETRY_ATTEMPTS = 6 };
912912/* Exponential backoff, doubling from 10ms: 10, 20, 40, 80, 160, 320 — ~630ms of
913913 * total patience on an ordinary build, and three further doublings (640, 1280,
914914 * 2560) to roughly 5s on a sanitized one. The waits follow from
915- * CBM_SPAWN_RETRY_ATTEMPTS above rather than being listed separately here, so
916- * changing the budget cannot leave this description behind.
915+ * CBM_SPAWN_RETRY_ATTEMPTS above and from the per-wait ceiling at
916+ * cbm_spawn_backoff, rather than being listed separately here, so changing the
917+ * budget cannot leave this description behind.
917918 *
918919 * The first version waited a flat 3 x 10ms, which was enough for a momentary
919920 * dip and NOT enough for the real thing: a CI runner building and testing in
@@ -948,13 +949,26 @@ static bool cbm_spawn_eagain_injected(void) {
948949}
949950#endif
950951
952+ /* The bound is on a single WAIT, and 2560ms (10ms << 8) is the largest wait
953+ * either budget produces today: 10..320 on an ordinary build, 10..2560 on a
954+ * sanitized one. So this changes nothing now — it changes what happens next.
955+ *
956+ * The clamp it replaces bounded `attempt` by CBM_SPAWN_RETRY_ATTEMPTS, which no
957+ * caller can reach: both retry loops return before passing the budget, so the
958+ * clamp never fired and the comment claiming "the budget is the only bound
959+ * needed" described a bound that did not exist. Doubling with nothing to stop
960+ * it is the actual risk, and it grows fast — a budget of 12 would make the last
961+ * wait ~20s and the total ~41s, which is precisely the "hang instead of fail
962+ * fast" the retry was written to avoid. Flattening at the ceiling keeps a
963+ * budget increase linear.
964+ *
965+ * Spelled as a shift so the value cannot overflow `long` on the way to being
966+ * clamped. */
967+ enum { CBM_SPAWN_BACKOFF_BASE_MS = 10 , CBM_SPAWN_BACKOFF_MAX_SHIFT = 8 };
968+
951969static void cbm_spawn_backoff (int attempt ) {
952- /* Cap the shift at the budget, not at a constant: with the sanitized budget
953- * of 9 a hard-coded 6 would flatten the last three waits to 320ms each
954- * instead of continuing to double. The budget is the only bound needed —
955- * callers never exceed it, and a second clamp would be dead code. */
956- int shift = attempt < CBM_SPAWN_RETRY_ATTEMPTS ? attempt : CBM_SPAWN_RETRY_ATTEMPTS ;
957- long ms = 10L << shift ;
970+ int shift = attempt < CBM_SPAWN_BACKOFF_MAX_SHIFT ? attempt : CBM_SPAWN_BACKOFF_MAX_SHIFT ;
971+ long ms = (long )CBM_SPAWN_BACKOFF_BASE_MS << shift ;
958972 struct timespec delay = {ms / 1000L , (ms % 1000L ) * 1000L * 1000L };
959973 (void )cbm_nanosleep (& delay , NULL );
960974}
0 commit comments