A lot of “embedded” libcs do some pretty sketchy things; frustration with this sort of thing is what prompted the development of musl. I imagine given its dependency on a linux kernel it wouldn’t work in the kind of environments the author is dealing with, but fwiw here’s rand.c:
More generally: any libc function may call malloc. If this matters to you, then you should look at the libc internals and audit any function that you care about. Folks that ship a libc need to think about this in a few places. For example, FreeBSD libc uses jemalloc, which uses locks to protect some data structures, but the locks call malloc and so have their own bootstrapping path.
The specification of a lot of functions doesn’t have a suitable failure mode, so no, they can’t really call malloc (and require it to succeed) without being non-conformant.
BTW, using rand is a bad idea since some implementations of it return extremely poor quality results. Use random instead. From the Linux man page:
on older rand() implementations, **and on current
implementations on different systems**, the lower-order bits are
much less random than the higher-order bits. Do not use this
function in applications intended to be portable when good
randomness is needed. (Use random(3) instead.)
In this case it’s not the global state per se, but that for some reason the global state is heap-allocated on first use, even though it just consists of a single integer.
The global state is heap-allocated because the function has to be re-entrant, or at least because the re-entrancy is enabled in the libc options. That means if you have multiple threads they each get their own rand() state… because rand() is designed around having implicit global state.
The global state required by the C standard is the seed used by rand() that can be set with srand(unsigned seed). I strongly prefer the rand_r(unsigned int *seed) function defined in the blog post since that makes the state explicit and up to the caller whether it is global.
You’re talking about per-thread state, i.e. a thread-local variable. It doesn’t necessarily need to be heap-allocated, although usually that’s an implementation detail of thread-local variables.
A lot of “embedded” libcs do some pretty sketchy things; frustration with this sort of thing is what prompted the development of musl. I imagine given its dependency on a linux kernel it wouldn’t work in the kind of environments the author is dealing with, but fwiw here’s rand.c:
User with the username “nomemory” submits a post about an unexpected malloc. Hmmm. ;)
Good article though, I just thought the coincidence was amusing.
:).
Got the nickname as silent protest to my parents who in 99 or 98 refused to buy me additional 32RAM so I can play Half Life…
I got past this unfortunate event, but I kept the username.
More generally: any libc function may call malloc. If this matters to you, then you should look at the libc internals and audit any function that you care about. Folks that ship a libc need to think about this in a few places. For example, FreeBSD libc uses jemalloc, which uses locks to protect some data structures, but the locks call malloc and so have their own bootstrapping path.
The specification of a lot of functions doesn’t have a suitable failure mode, so no, they can’t really call malloc (and require it to succeed) without being non-conformant.
That’s not true, async-signal-safety is a thing.
BTW, using rand is a bad idea since some implementations of it return extremely poor quality results. Use
random
instead. From the Linux man page:(Emphasis mine)
Also known as, “why implicit global state will always fuck you”.
In this case it’s not the global state per se, but that for some reason the global state is heap-allocated on first use, even though it just consists of a single integer.
The global state is heap-allocated because the function has to be re-entrant, or at least because the re-entrancy is enabled in the libc options. That means if you have multiple threads they each get their own
rand()
state… becauserand()
is designed around having implicit global state.The C standard has no such mandate for
rand()
.The global state required by the C standard is the seed used by
rand()
that can be set withsrand(unsigned seed)
. I strongly prefer therand_r(unsigned int *seed)
function defined in the blog post since that makes the state explicit and up to the caller whether it is global.rand()
needing global state yes, being re-entrant, no.Right, because the standard was written before my old ass was born, when threads didn’t exist on Unix.
You’re talking about per-thread state, i.e. a thread-local variable. It doesn’t necessarily need to be heap-allocated, although usually that’s an implementation detail of thread-local variables.
Ah you’re correct, I forgot thread-locals exist. My bad.