pub const STACK_SIZE: usize = _; // 110_985_216usizeExpand description
How much stack the runtime gives every thread it runs Cove on.
A tree-walking interpreter spends native stack per Cove frame, so
MAX_CALL_DEPTH keeps its promise only on a stack big enough to hold
that many frames. Nothing gave the runtime such a stack before: a spawned
task took the platform default of 2 MiB, and the entry took whatever the
process main thread happened to have, which is 8 MiB on macOS and Linux
and 1 MiB on Windows. In a debug build 8 MiB held 65 frames of the
cheapest recursion Cove can write, so the limit of 256 was reached only on
a release build’s main thread, and everywhere else an ordinary program
with no capability granted at all could end the process by recursing.
So the size is derived from the limits rather than chosen beside them.
Raising MAX_CALL_DEPTH raises this, which is the relationship the
limit’s promise rests on, and it is now arithmetic rather than a
coincidence that held on one thread of one profile. It works out at about
106 MiB in a debug build and about 8 MiB in a release one.
A number that large in a debug build is affordable because a thread stack
is reserved address space that commits a page at a time as it is touched.
That was checked rather than assumed: a debug run holding a hundred tasks
alive at once reached a maximum resident set of 14.9 to 15.1 MB under this
size and 15.07 MB under the old platform default of 2 MiB — a difference
smaller than the variation between runs — while reserving over 10 GiB of
address space. What the size costs is address space per live task and not
memory per live task, which is a trade a 64-bit host does not notice, and
max_tasks is the control for how many live tasks a run may hold in any
case.
An embedder that calls Interpreter::run_entry on a thread of its own
is the one case the runtime cannot size, and the one case where the
promise is the embedder’s to keep; see that method for what to do about
it.
cove_syntax’s MAX_NESTING_DEPTH answers the same question from the
other side. The parser is handed a thread rather than making one, so it
cannot size the stack and instead fixes the stack it is willing to promise
— 2 MiB, what an unsized thread has — and derives its limit from what a
level of nesting costs on it. Together the two bound both halves of a
.cove file’s route through the toolchain: reading it and running it.