Thread-Safe rand()
Anton's First Substack Post
One of my 2023 resolutions is to get back in to some writing. I have a homemade blog antongerdelan.net/blog but I’d like to give Substack a try. This post is a bit of an experiment with using Substack, so the content is somewhat simple today, but is a hint at what directions it might take.
rand() Implementations are Inconsistent
I’m building a Minecraft-esque hobby project, which uses a lot of random numbers for generating the voxel world, and for doing various other interesting things at run-time.
I’m using the C programming language, which has a pseudo-random number generator called rand() in the stdlib.h library. The numbers you get out from this function differ on different platforms, which is not ideal, so I created a very simple drop-in replacement:
#define APG_RAND_MAX 32767 // I think this is what Microsoft uses.
typedef unsigned long int apg_rand_t;
static apg_rand_t _seed = 1;
void apg_srand( apg_rand_t seed ) { _seed = seed; }
int apg_rand( void ) {
_seed = _seed * 1103515245 + 12345;
return (unsigned int)(_seed / ((APG_RAND_MAX + 1) * 2)) % (APG_RAND_MAX + 1);
}If I recall, I took the values and logic from a guide implementation. You can create a better pseudo-random sequence, but for my hobby program this was fine.
Multi-Threaded Path-Finding
At some point I added multi-threaded AI simulation for characters in the world, which helped organise the execution of 3D path-finding without disrupting the performance of the game.

The problem with the above code, as with rand() from the standard library, is that it uses a global variable to maintain state, which is fine for old-school single-threaded applications, but if I want to keep using it within worker threads then I will get race conditions.
I was going to replace my trivial copy of rand() with a more sophisticated function, but I decided instead on recreating the re-entrant rand_r(), also from stdlib.h. It is somewhat imprecise, as it admits in the man-page, but I thought that it would be more interesting to have the full set of drop-in functions.
int apg_rand_r( apg_rand_t* seed_ptr ) {
assert( seed_ptr );
if ( !seed_ptr ) { return 0; }
*seed_ptr = *seed_ptr * 1103515245 + 12345;
return (unsigned int)(*seed_ptr / ((APG_RAND_MAX + 1) * 2)) % (APG_RAND_MAX + 1);
}This has the same logic as the previous function, but the seed is user-supplied. So each thread will have its own seed variable that it passes between calls to apg_rand_r(). An example use follows:
apg_rand_t initial_seed = time( NULL ); // Equiv. to srand().
apg_rand_t working_sequence = initial_seed;
int random_result = rand_r( &working_sequence );Floating Point rand()
A random number between 0.0 and 1.0 is quite useful for procedural generation, so I also added floating point convenience variants of the rand() functions:
float apg_randf( void ) {
return (float)apg_rand() / (float)APG_RAND_MAX;
}float apg_randf_r( apg_rand_t* seed_ptr ) {
assert( seed_ptr );
if ( !seed_ptr ) { return 0.0f; }
return (float)apg_rand_r( seed_ptr ) / (float)APG_RAND_MAX;
}I think I got this idea from a post about how Minecraft was created, which I would link, but it was a long time ago, and I have old-person grade memory now!
Conventions and Caveats
I tried to stay as close to the interface of rand() functions as possible, such that I could use these functions as drop-in replacements to existing code.
The original functions use
unsignedseeds, but I wanted to useunsigned long. I just made this atypedefin case I needed to switch it back tounsignedfor backwards compatibility.Integers in interfaces are vulnerable to precision and sign accidental misuse, so I would usually put this sort of parameter in a struct to provide some harder type protection. Not this time though.
I have these functions in my apg.h little snippet library. Type and function names are preceded by
apg_. It’s an unofficial convention to use a prefix for C libraries, since C doesn’t have namespaces. Likewise, constants are prefixed byAPG_. Types I post-fix with_tfor type, which is sort of naughty because POSIX claims to reserve that exclusively, but I’m not fussed about that.You might notice, despite functions returning an
int, that they are cast tounsigned intfirst. This is done deliberately to avoid returning negative numbers. The functions should probably just returnunsigned int. This wasn’t changed for consistency with the originals.
Thoughts on Substack
I can’t see any obvious way to change the typeface and theme yet. If you’re reading this in a sans-serif font then I figured at least some of it out!
It’s a bit unresponsive to type into the web interface, which is quite frustrating, and it doesn’t seem to pick up every keystroke when I type quickly.
If this works well I might replace my crummy old blog. I have a few ideas for subject streams to write about that might be of value to readers. I’d be interested in getting a feel for the reach and accessibility versus publishing books, for example. Text-to-speech for listeners, and text size adjustment for readers could be a big win.
I’ve never been a huge fan of video tutorials since they are very time-intensive to create and also to listen to, compared to written text. And you can follow code much more easily than from a screen recording. Perhaps this could be a useful middle ground, or where a mixed approach would work well.
If you’d like to see more of this sort of thing (but maybe more useful!) subscribe/share/say hi!


