== Stack size is invisible in C and the effects on "portability" Somewhat recently I read Ariadne Conill's [[understanding thread stack sizes and how alpine is different https://ariadne.space/2021/06/25/understanding-thread-stack-sizes-and-how-alpine-is-different/]] ([[via https://lobste.rs/s/ms3nsg/understanding_thread_stack_sizes_how]]), which is in part about how [[Alpine Linux https://www.alpinelinux.org/]] has a very low default thread stack size, unlike other things, and this can cause program crashes. As part of this, Conill says: > In general, it is my opinion that if your program is crashing on > Alpine, it is because your program is dependent on behavior that is > not guaranteed to actually exist, which means your program is not > actually portable. When it comes to this kind of dependency, the > typical issue has to deal with the *thread stack size limit*. Conill also sort of calls out glibc-based Linux for having by far the largest default thread stack size at 8 MiB, and says: > [...] This leads to crashes in code which assumes a full 8MiB is > available for each thread to use. The practical problem with this view is that stack size is invisible in C, and especially it's not part of the portable C API and generally not part of either the platform API or ABI. Unlike _malloc()_, which can at least officially fail, the stack is magic; your code can neither guard against hitting its size limit nor check its limits in any portable way. Nor can you portably measure how much stack size you're using or determine how much stack size it may require to call library functions (this is part of [[how the C library API is under-specified GoCLibraryAPIIssues]]). If a limitation exists but its exact parameters are invisible to you, running into it (and crashing) doesn't make your program "not actually portable" in any pejorative sense, it makes it unfortunate. That your program doesn't run in some limited environments is perhaps not ideal but it is not particularly your fault. Also, since there is no (reasonable) way to test or mitigate stack size issues in C, all that both programmers and library implementers can reasonably do is operate by superstition and supposition. In light of this, glibc's decision to use a large default thread stack size is entirely reasonable; it's pretty much the safest choice, especially since glibc makes it the same as the usual default program stack size. Attempting to limit stack space usage without the tools to measure it is probably not as dangerous as trying to optimize your code without doing performance testing, but it's probably not going to yield really good results either. Some people would like C programmers to be efficient (ie limited) in their use of stack space. Apart from anything else I might feel about this, I will say that it's important for people to be able to measure and monitor anything that you want them to be efficient with. If you want me to minimize my code's power usage but don't provide me with tools to measure that, you aren't likely to get much in practice (and what I do without measurement may make it worse). (Technically speaking it's possible to assess and measure stack size usage of C code if you try hard enough. For example, you can have a great test suite and conduct binary searches to determine at what thread stack size your code starts to crash under test. Program analysis techniques may also be tempting, but remember that [[your platform C library probably doesn't have any specific stack usage promises ../unix/UnixAPIAndCRuntime]].)