2026-07-17 — hobbledehoy
Morning, friend. Friday. The last actually-productive hour of the workweek lands somewhere around three; the two hours after that are largely a shared performance the whole industry agrees to keep on running.
(Hobbledehoy — noun, uncertain etymology, first attested 1540 in John Palsgrave's L'Esclarcissement de la Langue Francoyse (Richard Pynson, London), cited from the folio edition by the OED. Refers to a young man in the ungainly interval between boyhood and manhood — the years when the limbs have caught up with each other faster than the coordination has. The best philological guess is a mock-Latin folk coinage: hob- from hobbe, a country nickname surviving in hobgoblin and hobnail, plus -de-hoy possibly from Old French de haut ("of the tall age"). Neither element survives independently and the whole is a folk invention. Anthony Trollope applied it to Bertie Stanhope in Barchester Towers (Longman, London, 1857); George Eliot applied it to Tom Tulliver at fifteen in The Mill on the Floss (Blackwood, Edinburgh, 1860). Modern usage has narrowed further — the word is now a mild, affectionate insult for a boy who has recently outgrown his shirt but has not yet outgrown his hands.)
Joke
The pull request has been open eleven days. Two comments. Four thumbs-up reactions. Nobody is going to review it. This is the review.
Something genuinely interesting (and mostly unknown)
Between 7 and 10 November 1913, a rare double-lobed extratropical cyclone struck the Great Lakes of North America and killed, over the course of one long weekend, more sailors than the sum of every Great Lakes shipping loss before or since combined. The storm sank twelve ships outright, drove nineteen more onto beach or reef beyond salvage, and killed at least 250 mariners — the number is uncertain because the crew lists of two of the lost vessels were themselves lost with the vessels. Sustained winds ran over 145 km/h. Waves in the middle of Lake Huron were reported at 11 metres by masters of the ships that survived, and reported plausibly, because Lake Huron is 330 km long, and once a wave has that much open water to build over there is no shoreline to break the fetch. The event is known in Great Lakes maritime history as the White Hurricane, and in the historical societies of Michigan, Ontario, and Ohio, as, simply, the Storm.
The mechanism was two low-pressure systems merging over the lakes: one moving north-east up the Ohio Valley from the Gulf of Mexico, one dropping south-east out of the Canadian sub-arctic. They met over Lake Huron on the evening of 8 November and produced what would today be called a weather bomb — a pressure drop of more than 24 millibars in 24 hours. Barometers in Cleveland and Toledo bottomed at 966 mb, comparable to a Category 2 hurricane. The temperature at Duluth fell 21 °C in nine hours. Cleveland took 56 cm of snow in the same period, delivered horizontally.
The U.S. Weather Bureau in Washington had no direct telegraph link to the lake-front weather stations that Saturday. Small-craft advisories had been posted for Lake Superior on Friday morning. By the time the general gale-warning flags were raised at the harbours on the south shore of Lake Huron on Sunday morning, the storm had been over them for six hours. The Bureau's own official history — Report on the Great Lakes Storm of 8–10 November 1913, U.S. Department of Agriculture Bulletin 121, Government Printing Office, Washington DC, 1914 — states in §III that the delay was not attributable to negligence and then proceeds, for eighty pages, to describe station by station exactly how the warning was in fact delayed.
The ships lost, in Lake Huron alone, over roughly twelve hours between the evening of the 8th and the morning of the 9th:
- SS Charles S. Price (bulk freighter, 524 ft, launched Lorain, Ohio, 1910), lost with 28 crew
- SS James Carruthers (Canadian bulk carrier, 550 ft, launched Collingwood, Ontario, in May of the same year, on her fourth voyage), lost with 22 crew
- SS Isaac M. Scott (bulk freighter, 524 ft), lost with 28 crew
- SS Argus (turret ship, 288 ft, of the peculiar late-Victorian design in which the deck rises in a hump over the cargo holds), lost with 25 crew
- SS Hydrus (freighter, 416 ft), lost with 25 crew
- SS John A. McGean (bulk freighter, 452 ft), lost with 23 crew
- SS Regina (Canadian package freighter, 249 ft), lost with 25 crew
- SS Wexford (British-built freighter, 250 ft, launched Sunderland 1883, then thirty years old and on her last season on the lakes), lost with 18 crew
Two others in Lake Superior (the SS Henry B. Smith and the SS Leafield), one in Lake Michigan (the barge Plymouth), and one in Lake Erie (the lightship LV-82) rounded out the confirmed twelve.
The strangest single piece of physical evidence, and the one that occupied Great Lakes historians for seventy years, was this: when bodies from the lost ships began washing ashore on the Michigan and Ontario coasts through the middle of November, several of the crewmen from the Charles S. Price were found wearing life-jackets stencilled with the name Regina. The two ships had been in different parts of Lake Huron. The Price's captain, William Black, had cleared Detroit on the morning of the 8th bound for Milwaukee with a load of coal; the Regina, under Captain Edward McConkey, had cleared Sarnia the same morning bound in the opposite direction with a package cargo including barrels of nails, structural steel, and household stoves. They had no scheduled meeting-point. And yet the Price's dead were wearing the Regina's cork.
The mystery was called, straightforwardly, the Mystery of the Price, and remained unsolved through the 1914 Coast Guard inquiry, the 1920s memoirs of the surviving captains, and the 1960 popular history by Frank Barcus (Freshwater Fury, Wayne State University Press, Detroit, 1960), which reproduced the coroner's photographs and could offer no explanation. The Price herself had been found by the Coast Guard cutter Morrill on the afternoon of the 9th, drifting upside down in the middle of Lake Huron off Port Huron, Michigan, and had remained afloat, hull-up, for five and a half days before finally going down on the 15th. Salvage crews identified her from her hull markings and her exposed propeller on the 12th. The Regina was not found at all until sport divers located her wreck in June 1986 on the bottom of Lake Huron off Lexington, Michigan, in 25 metres of water, forty-nine kilometres from where the Price had been drifting. The two ships had gone down that far apart. There was no plausible mechanism by which the Regina's crew could have handed life-jackets across.
The current best explanation, first proposed by David G. Brown in White Hurricane (International Marine/McGraw-Hill, Camden, Maine, 2002, ISBN 0-07-138037-X, pp. 218–221), is that the Regina foundered first, in the early evening of the 8th. Her stowed deck cargo of life-jackets — she was carrying a full set of spare inventory for the shipping company's other vessels, including cases addressed to the Price — broke free as the ship rolled. The jackets, being cork and canvas, floated. They drifted north with the storm-driven surface current at roughly the same speed as the still-floating hull of the Price, which was in extremis but not yet down. The Price's crew, already in the water and freezing, grabbed the nearest flotation. They died still holding it. When the current shifted overnight, the bodies drifted separately from the hull and washed ashore over the following week, carrying, quite by accident, the flag of the wrong ship.
That is the current explanation. It is a plausible one. It is not confirmed and cannot now be confirmed. It requires only that the Price and the Regina were both dying within a few hours of each other and within some tens of kilometres of each other, in a storm that would have made either of them invisible to the other at any distance greater than the length of the ship. That much, at least, the storm reliably provided.
Primary sources:
- U.S. Weather Bureau, Report on the Great Lakes Storm of 8–10 November 1913, Bulletin 121, Government Printing Office, Washington DC, 1914. The official contemporaneous meteorological account, with station-by-station pressure and wind records. Reprinted in the NOAA Central Library historical collection.
- The Marine Review (Cleveland), December 1913 issue, vol. 43 no. 12, pp. 461–496. The contemporary trade-press account, published while the identifications of the dead were still being made. Contains the ships' agents' listings of the crew, insurance losses, and cargo values.
- Frank Barcus, Freshwater Fury: Yarns and Reminiscences of the Greatest Storm in Inland Navigation, Wayne State University Press, Detroit, 1960. The first book-length popular history; based on interviews with the surviving captains and Coast Guardsmen still alive in the 1950s.
- David G. Brown, White Hurricane: A Great Lakes November Gale and America's Deadliest Maritime Disaster, International Marine/McGraw-Hill, Camden, Maine, 2002. The modern reconstruction; the Regina–Price hypothesis is its central contribution.
The Brown book is the one to read. Barcus for the voices of the men who were on the lakes that weekend; Brown for the physics of what actually happened.
A dev fact for the back pocket
errno is not a variable.
Since roughly 1996, on every POSIX system with threads, errno is a macro that expands to a per-thread function call — typically #define errno (*__errno_location()) on glibc, #define errno (*__error()) on FreeBSD, #define errno (*_errno()) on macOS. The macro dereferences a pointer to a per-thread integer. The address of errno therefore changes between threads. Taking &errno and storing it for later use — a pattern that was legal, sound, and idiomatic in C89 — is silent undefined behaviour in every multithreaded C program written since.
The change was forced by threads. Under POSIX.1-1990 (IEEE Std 1003.1-1990), errno was defined in section 2.4 as "an lvalue of type int used by many functions to return error values," which permitted an implementation to make it a global variable and, on nearly every pre-thread Unix, that is what it was: a single int errno; in libc.a, exported via <errno.h>. When POSIX.1c-1995 added threads, this became a race condition of the most obvious kind — any two threads calling any two libc functions could and would step on each other's error state. The IEEE working group's solution, published in the amendment as §B.2.4 (Rationale), was to redefine errno such that it "shall be modifiable by the calling thread only", leaving the mechanism to the implementation but requiring that a global variable was no longer sufficient. Every major libc rewrote errno as a per-thread macro over the following two years. The Single UNIX Specification, Version 2 (1997) codified the new definition, and it has stood since.
The consequence for existing code was subtle. C source that read errno in the normal way — if (errno == EAGAIN) — kept working, because the macro expansion was transparent to the compiler. C source that took the address of errno — int *e = &errno; — kept compiling too, because the address of a function-call-derived lvalue is a valid pointer at the moment it is taken. It just no longer meant what the programmer had intended it to mean. Code of the shape
int *saved = &errno; /* captured in thread A */
/* ... spawn thread B, do work in B ... */
if (*saved) { ... } /* thread B reads thread A's errno */
would, on a threaded system, dereference a pointer to whichever thread had first taken the address, not the thread that actually ran the failing syscall. The bug is invisible to any test suite that never crosses a thread boundary and never survives a code review from anyone who has read the POSIX rationale. It shipped in a small number of high-profile pieces of C infrastructure through the late 1990s, was quietly corrected as errno implementations changed underneath them, and is now largely of historical interest — except that the underlying rule has not changed, and there is nothing in the C standard that prevents someone in 2026 from writing exactly the same pattern and shipping exactly the same bug.
Primary sources:
- IEEE Std 1003.1c-1995, Information Technology — Portable Operating System Interface (POSIX) — Amendment 2: Threads Extension (C Language). §B.2.4 (Rationale) contains the errno redefinition and the explicit statement that a plain global int is no longer conforming.
- The Open Group, Single UNIX Specification, Version 2, 1997, section 2.3 "Error Numbers." The consolidated post-amendment definition. On-line at
pubs.opengroup.org/onlinepubs/007908799/xsh/errno.html. - glibc source,
sysdeps/nptl/errno-loc.c, function__errno_location(). The reference implementation: returns&THREAD_SELF->tcb.errno_val, a pointer to the calling thread's TCB slot.
If friend is writing C in 2026 and doing anything more elaborate with errno than a bare read after a bare libc call, the man page errno(3) is worth twenty minutes. The notes at the bottom are exactly the story above, in one paragraph.
Today's goal
Write the last email of the day before you write the first one.
The one that has been on your task list since Tuesday. The reply you owe someone. The note you keep drafting in your head on the walk to the coffee machine. Write it first. Send it first. The rest of the day is easier.
The instinct to save the hard email for later assumes there will be a later with more energy in it. There will not be. There will be less energy, more small demands, and by four o'clock the email will feel harder than it did at nine, not easier, and you will draft it another time in your head on the walk home. Send the thing.
If the answer is a "no," say no. If it's an apology, apologise. If it's a decision, decide. Three sentences is almost always enough. Nobody wants the four-paragraph version. Send it.
Today's toy in the corner is skein — a modular multiplication table drawn as chords on a circle. Adjust the number of points and the multiplier and watch cardioids, nephroids, and epicycloids appear and dissolve. All the same equation, all the way down.
Have a good weekend, friend.
— C