From d6002d68a2f15a286694fc36a8dfc599d4f5be68 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 3 Mar 2025 15:29:33 -0500 Subject: [PATCH 1/2] Make `os2.random_string` use `context.random_generator` This removes the data race caused by multiple threads using the unprotected global `random_string_seed`, so long as no two threads share the same random generator; this is the default case. Additionally, `os2.random_string` now takes into account the full buffer slice given to it. --- core/os/os2/internal_util.odin | 47 +++++++--------------------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 164e1e1be..ce253d17b 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -3,6 +3,7 @@ package os2 import "base:intrinsics" import "base:runtime" +import "core:math/rand" // Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix @@ -84,45 +85,15 @@ concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: st return string(buf), nil } - - -@(private="file") -random_string_seed: [2]u64 - -@(init, private="file") -init_random_string_seed :: proc() { - seed := u64(intrinsics.read_cycle_counter()) - s := &random_string_seed - s[0] = 0 - s[1] = (seed << 1) | 1 - _ = next_random(s) - s[1] += seed - _ = next_random(s) -} - -@(require_results) -next_random :: proc(r: ^[2]u64) -> u64 { - old_state := r[0] - r[0] = old_state * 6364136223846793005 + (r[1]|1) - xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 - rot := (old_state >> 59) - return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) -} - @(require_results) random_string :: proc(buf: []byte) -> string { - @(static, rodata) digits := "0123456789" - - u := next_random(&random_string_seed) - - b :: 10 - i := len(buf) - for u >= b { - i -= 1 - buf[i] = digits[u % b] - u /= b + for i := 0; i < len(buf); i += 16 { + n := rand.uint64() + end := min(i + 16, len(buf)) + for j := i; j < end; j += 1 { + buf[j] = '0' + u8(n) % 10 + n >>= 4 + } } - i -= 1 - buf[i] = digits[u % b] - return string(buf[i:]) + return string(buf) } From 2d0dc44636c3f20bb1c41433a5d9cbd52098792b Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 3 Mar 2025 19:18:54 -0500 Subject: [PATCH 2/2] Shorten buffers used for `os2.random_string` results This is needed now that `os2.random_string` fills the whole slice. --- core/os/os2/temp_file.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index 5ca4e1453..e93117f02 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -20,7 +20,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { prefix, suffix := _prefix_and_suffix(pattern) or_return prefix = temp_join_path(dir, prefix) or_return - rand_buf: [32]byte + rand_buf: [10]byte name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) attempts := 0 @@ -52,7 +52,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) prefix, suffix := _prefix_and_suffix(pattern) or_return prefix = temp_join_path(dir, prefix) or_return - rand_buf: [32]byte + rand_buf: [10]byte name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) attempts := 0