summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-10-30 11:56:05 -0400
committerAndrew Opalach <andrew@akon.city> 2025-10-30 11:56:05 -0400
commit1b15b6ba0a78e0fe87716cc1df07f092c64dfefb (patch)
treec784e9a4a39521f00a62723bfa2aa85850bd081d
parent00c292b5bd4f6cf24287c7581d54a60f4b6fdcb7 (diff)
downloadlibalabaster-1b15b6ba0a78e0fe87716cc1df07f092c64dfefb.tar.gz
libalabaster-1b15b6ba0a78e0fe87716cc1df07f092c64dfefb.tar.bz2
libalabaster-1b15b6ba0a78e0fe87716cc1df07f092c64dfefb.zip
random: Try to ensure rand() state is thread-local
Signed-off-by: Andrew Opalach <andrew@akon.city>
-rw-r--r--include/al/random.h34
-rw-r--r--meson.build10
-rw-r--r--src/random.c6
3 files changed, 47 insertions, 3 deletions
diff --git a/include/al/random.h b/include/al/random.h
index f92a77b..3df3140 100644
--- a/include/al/random.h
+++ b/include/al/random.h
@@ -5,23 +5,53 @@
#include "macros.h"
#define AL_RAND_MAX RAND_MAX
+#define AL_RAND_INIT_SEED 1738
extern u32 al_rand_seed;
+
extern __thread bool al_rand_set;
+#ifndef AL_SRAND_THREAD_LOCAL
+extern __thread u32 al_rand_state;
+#endif
static inline void al_rand_init(u32 seed)
{
al_rand_seed = seed;
}
+// @TODO: Replace this with an implementation of any PRNG algorithm where
+// we can control the state and make it thread-local. Trying to predict the
+// state in libc is pretty dumb.
+// al_rand() is equivalent to libc rand(), as such there is no expectation
+// of it being an actually good random number generator.
+#ifdef AL_SRAND_THREAD_LOCAL
+#define rrand() rand()
+#else
+#define rrand() rand_r(&al_rand_state)
+#endif
static inline s32 al_rand(void)
{
if (UNLIKELY(!al_rand_set)) {
- // srand() is thread-local in Windows CRT.
+#ifdef AL_SRAND_THREAD_LOCAL
srand(al_rand_seed);
+#else
+ al_rand_state = al_rand_seed;
+#endif
+ // Call rand() once after setting the seed incase it returns the seed
+ // on the first call, like on Windows. Having the first result potentially
+ // be so similar run-to-run would no longer even be pseudo-random.
+ // https://stackoverflow.com/a/76676396
+ rrand();
al_rand_set = true;
}
- return rand();
+ return rrand();
+}
+#undef rrand
+
+// https://c-faq.com/lib/randrange.html
+static inline s32 al_random_int(s32 min, s32 max) // [MIN, MAX]
+{
+ return min + al_rand() / (AL_RAND_MAX / (max - min + 1) + 1);
}
#endif // _AL_RANDOM_H
diff --git a/meson.build b/meson.build
index c948887..0dffee3 100644
--- a/meson.build
+++ b/meson.build
@@ -8,6 +8,7 @@ compiler = meson.get_compiler('c')
is_debug = get_option('debug')
is_msvc = compiler.get_id() == 'msvc' or compiler.get_id() == 'clang-cl'
is_msvc_specifically = compiler.cmd_array()[0] == 'cl'
+is_windows = host_machine.system() == 'windows'
supports_needed_gnu_extensions = not is_msvc_specifically
@@ -57,6 +58,15 @@ if is_msvc_specifically
alabaster_args += ['/arch:AVX']
endif
+if compiler.has_function('rand_r', prefix: '#define _POSIX_C_SOURCE 200112L\n#include <stdlib.h>')
+elif is_windows # This is supposed to be a check for if libc = Microsoft CRT.
+ # https://learn.microsoft.com/en-us/previous-versions/f0d4wb4t(v=vs.140)?redirectedfrom=MSDN
+ # "The srand function sets ... in the current thread."
+ alabaster_args += ['-DAL_SRAND_THREAD_LOCAL']
+else
+ error('No thread-local rand().')
+endif
+
if supports_needed_gnu_extensions
alabaster_args += ['-DAL_HAVE_GNU_EXTENSIONS']
endif
diff --git a/src/random.c b/src/random.c
index 1bbf152..fb059a0 100644
--- a/src/random.c
+++ b/src/random.c
@@ -1,4 +1,8 @@
#include "../include/al/random.h"
-u32 al_rand_seed = 1738;
+u32 al_rand_seed = AL_RAND_INIT_SEED;
+
__thread bool al_rand_set = false;
+#ifndef AL_SRAND_THREAD_LOCAL
+__thread u32 al_rand_state = AL_RAND_INIT_SEED;
+#endif