skip to content

A firmware update routine acquires four resources in order and the fourth fails — why is a forward jump to a cleanup ladder defensible for releasing the three already held?

level: seniorimportance: should knowfreq 38%

answer

  1. N acquired, K-1 to release
  2. reverse order, one copy each
  3. forward only, one target, never inward
  4. the alternatives duplicate or nest
  5. unnecessary where scope exit releases

basics

~20 s

Because the jump is forward only, targets one label, and never enters a block partway, so it keeps the single-entry discipline while the structured alternatives either duplicate the release sequence or deepen the routine one level per resource.

solid answer

~50 s

The ladder answers a specific shape: N resources acquired in order, and a failure at step K must release exactly the K-1 already held, in reverse. The structured alternatives price badly. Nesting each acquisition inside the previous success arm adds a level per resource and repeats release code in each alternative arm. A flag per resource puts the acquisition state into variables the cleanup then re-derives. The ladder instead uses **forward jumps to labelled release points chained in reverse order**, so each failure site names where unwinding starts and fall-through does the rest — one copy of each release, no nesting. That is the shape the rebuttal **Structured Programming with go to Statements** defended: a forward-only jump to a single target that never re-enters a block, used where structure would duplicate code. Where the language binds release to leaving a block, the ladder is unnecessary and you should use that instead.

code

pseudocode · 17 lines
pseudocode
if not acquire_power() then jump to done end
if not acquire_storage() then jump to release_power end
if not acquire_buffer() then jump to release_storage end
if not acquire_window() then jump to release_buffer end

status = write_update()

release_window:
    release(window)
release_buffer:
    release(buffer)
release_storage:
    release(storage)
release_power:
    release(power)
done:
    return status

go deeper

for a junior

Recall the obligation before the structure: a failure partway through a sequence of acquisitions must release exactly what was already acquired, in reverse order.

for a middle

Explain why the jump-free versions cost what they cost — duplicated release sequences in the alternative arms, or a nesting level per resource — and what the ladder buys instead.

for a senior

Show that you judge the jump by its properties: forward only, one target, never into a block, short span. Then say where the pattern is the wrong answer because scope exit already releases.

for a principal

The call you own is the standard. Naming the two survivor shapes and requiring everything else to be structured is enforceable; a blanket ban produces flag-threaded routines your reviewers will approve.

## The shape of the problem A routine acquires a series of resources in order: power to the target device, a handle on its storage, a decoded image buffer, an exclusive write window. Each acquisition can fail, and a failure at step K must release the **K-1** resources already held, **in reverse order of acquisition**, then report. Nothing else in the routine is difficult; the unwinding is the whole difficulty. ## The four ways out, priced | Structure | Copies of each release | Nesting added | Failure mode | |---|---|---|---| | Nested success arms | One per alternative arm | One level per resource | Copies drift out of step | | Flag per resource | One, plus a test each | None | Cleanup re-derives what the code already knew | | Extracted unwinding routine | One | None | Every acquired handle must be passed or bundled | | Forward jumps to a ladder | One | None | Only correct if the labels are chained in reverse | The first is the version a keyword ban produces, and its cost grows with the number of resources: four acquisitions give four alternative arms, each carrying a release sequence, and the fifth resource added next quarter has to be woven into all of them. The second replaces control flow with state that the cleanup then has to interpret. The third is genuinely good where the handles travel together. The ladder is the cheapest of the four when they do not. ## What makes the jump honest here The defence is not `jumps are fine after all`. It is a set of properties this particular jump has: - **Forward only.** Control never returns upward, so no ad-hoc loop is formed. - **A single target per site.** Each failure names exactly one label, and each label's predecessor set is small and visible on the same screen. - **Never into a block.** The labels sit at the routine's tail, at the top level, so nothing is entered partway and no opening statement is skipped. - **Short span.** The target is within a screen of the jump, so the reader can see both. - **Named for its job.** A label called `release_storage` states what is true when control arrives. Every one of those is aimed at the original objection: the cost of a jump is an open predecessor set and a history the text does not show. Constrain the direction and the target and that cost collapses to almost nothing, while the duplication you avoided was real. ## The rebuttal's three cases The 1974 reply **Structured Programming with go to Statements** did not argue the objection was wrong. It argued the blanket reading was, and named cases where the jump-free version is worse: the **loop-and-a-half**, where the exit condition falls naturally in the middle of the body and the alternatives duplicate the pre-test work; the **error exit**, of which the cleanup ladder is the canonical form; and hot inner loops, an efficiency argument from a time when compilers did far less. Treat the first two as live and the third as something to measure before you claim it, because the code generators changed and the argument did not. ## The second survivor The other place an unstructured jump persists in modern code is the **state machine written as a loop over a current-state variable**, where each arm ends by assigning the next state. That assignment *is* a jump — control resumes at the top and dispatches elsewhere. It survives for the same reason the ladder does, but through different properties: the set of targets is **enumerable** because it is a declared set of states, the jump is written as **data** and so can be tabulated, tested and drawn, and the dispatch point is **one place** rather than scattered labels. The general lesson generalises: a jump is affordable exactly when its targets are few, named and reachable from a point the reader can find. ## Where the ladder should not appear Languages differ on this, and the difference decides the answer. Where a language binds release to leaving a block — a construct that runs cleanup when a scope ends, however it ends — the unwinding is already expressed, in order, next to the acquisition, and writing a ladder instead is worse on every axis. The ladder earns its place only where no such binding exists and the handles do not naturally travel as one bundle. A candidate who reaches for it unconditionally has learned a pattern rather than a tradeoff.

  • What breaks if the ladder's labels are written in acquisition order rather than reverse?
    A failure at the third acquisition jumps to the first release and falls through to the later ones, releasing resources that were never acquired and skipping the one that was. The reverse chaining is the entire correctness argument of the pattern: each label releases its own resource and then falls into the release of everything acquired before it.
  • A protocol handler loops over a current-state variable and assigns the next state at the end of each arm — is that a disguised jump?
    Yes, and a defensible one. The assignment transfers control to a different arm on the next pass, so the state variable is the jump target. What redeems it is that the targets form a declared, enumerable set, the transfer is data you can tabulate and test, and every path goes through one dispatch point the reader can find.
  • When would you extract the unwinding into its own routine instead of writing a ladder?
    When the acquired handles already travel together, or can be bundled cheaply, so the unwinding routine takes one argument and releases whatever is present. That gives one copy of each release with no jumps at all. The ladder wins when bundling would mean inventing a structure whose only purpose is to make cleanup callable.
  • Does the efficiency case for a jump in a hot inner loop still apply?
    Treat it as unproven until measured. The argument was made when code generators did much less transformation of loops and branches than they now do, so the same source shape no longer implies the same emitted code. The reasoning cost of the jump, meanwhile, is unchanged — so the burden of proof sits with the claim.

saying these in an interview costs you the question

  • Says the rebuttal argued the original objection was simply wrong
  • Treats every jump as equally harmful, including a forward-only cleanup exit
  • Claims a flag per resource is cleaner than a ladder
  • Repeats the hot-inner-loop efficiency case without measuring it
  • Writes the ladder even where leaving a block already releases
  • Orders the release labels the same way as the acquisitions