|
1 | 1 | //! Rust implementation of C library functions `rand` and `srand`
|
2 | 2 | //!
|
3 | 3 | //! Licensed under the Blue Oak Model Licence 1.0.0
|
4 |
| -use core::ffi::{c_int, c_uint}; |
| 4 | +use core::{ |
| 5 | + ffi::{c_int, c_uint}, |
| 6 | + sync::atomic::Ordering, |
| 7 | +}; |
5 | 8 |
|
6 |
| -struct GnuRand { |
7 |
| - r: [u32; 344], |
8 |
| - n: usize, |
9 |
| -} |
10 |
| - |
11 |
| -impl GnuRand { |
12 |
| - pub fn new(mut seed: u32) -> GnuRand { |
13 |
| - let mut r = [0u32; 344]; |
14 |
| - |
15 |
| - if seed == 0 { |
16 |
| - // Make sure seed is not 0 |
17 |
| - seed = 1; |
18 |
| - } |
| 9 | +use portable_atomic::AtomicU32; |
19 | 10 |
|
20 |
| - r[0] = seed; |
21 |
| - for i in 1..31 { |
22 |
| - // This does: |
23 |
| - // state[i] = (16807 * state[i - 1]) % 2147483647; |
24 |
| - // but avoids overflowing 31 bits. |
25 |
| - let hi = (r[i - 1] / 127773) as i32; |
26 |
| - let lo = (r[i - 1] % 127773) as i32; |
27 |
| - let mut word = 16807 * lo - 2836 * hi; |
28 |
| - if word < 0 { |
29 |
| - word += i32::MAX; |
30 |
| - } |
31 |
| - r[i] = word as u32; |
32 |
| - } |
33 |
| - for i in 31..34 { |
34 |
| - r[i] = r[i - 31]; |
35 |
| - } |
36 |
| - for i in 34..344 { |
37 |
| - r[i] = r[i - 31].wrapping_add(r[i - 3]); |
38 |
| - } |
39 |
| - |
40 |
| - GnuRand { r, n: 0 } |
41 |
| - } |
42 |
| - |
43 |
| - pub fn next(&mut self) -> i32 { |
44 |
| - let x = self.r[(self.n + 313) % 344].wrapping_add(self.r[(self.n + 341) % 344]); |
45 |
| - self.r[self.n % 344] = x; |
46 |
| - self.n = (self.n + 1) % 344; |
47 |
| - (x >> 1) as i32 |
48 |
| - } |
49 |
| -} |
50 |
| - |
51 |
| -static mut RAND: Option<GnuRand> = None; |
| 11 | +// static mut RAND: Option<GnuRand> = None; |
| 12 | +static RAND_STATE: AtomicU32 = AtomicU32::new(0x0); |
52 | 13 |
|
53 | 14 | /// Rust implementation of C library function `srand`
|
54 |
| -/// |
55 |
| -/// Relies on [`critical-section`](https://docs.rs/critical-section/1.2.0/critical_section/) for thread-safety |
56 | 15 | #[cfg_attr(feature = "rand", no_mangle)]
|
57 | 16 | pub extern "C" fn srand(seed: c_uint) {
|
58 |
| - let rnd = GnuRand::new(seed); |
59 |
| - critical_section::with(|_| unsafe { RAND = Some(rnd) }); |
| 17 | + RAND_STATE.store(seed, Ordering::Release); |
60 | 18 | }
|
61 | 19 |
|
62 | 20 | /// Rust implementation of C library function `rand`
|
63 | 21 | ///
|
64 |
| -/// Relies on [`critical-section`](https://docs.rs/critical-section/1.2.0/critical_section/) for thread-safety |
| 22 | +/// Returns a pseudo-random integer in the range 0 to `RAND_MAX` (inclusive). |
| 23 | +/// May produce the same value in a row if called from multiple threads on platforms not supporting CAS operations. |
65 | 24 | #[cfg_attr(feature = "rand", no_mangle)]
|
66 | 25 | pub extern "C" fn rand() -> c_int {
|
67 |
| - critical_section::with(|_| { |
68 |
| - let rnd = unsafe { RAND.get_or_insert_with(|| GnuRand::new(1)) }; |
69 |
| - rnd.next() |
70 |
| - }) |
| 26 | + // Atomically update the global LFSR state using compare_and_swap if available |
| 27 | + #[allow(dead_code)] |
| 28 | + fn with_cas() -> c_int { |
| 29 | + let mut current_state = RAND_STATE.load(Ordering::Relaxed); |
| 30 | + let mut new_state = current_state; |
| 31 | + let mut result = unsafe { crate::rand_r(&mut new_state as *mut _) }; |
| 32 | + |
| 33 | + loop { |
| 34 | + match RAND_STATE.compare_exchange_weak( |
| 35 | + current_state, |
| 36 | + new_state, |
| 37 | + Ordering::SeqCst, |
| 38 | + Ordering::Relaxed, |
| 39 | + ) { |
| 40 | + Ok(_) => break, |
| 41 | + Err(c) => current_state = c, |
| 42 | + } |
| 43 | + new_state = current_state; |
| 44 | + result = unsafe { crate::rand_r(&mut new_state as *mut _) }; |
| 45 | + } |
| 46 | + |
| 47 | + result as _ |
| 48 | + } |
| 49 | + // Fallback to non-atomic operation if compare_and_swap is not available |
| 50 | + #[allow(dead_code)] |
| 51 | + fn without_cas() -> c_int { |
| 52 | + let mut current_state = RAND_STATE.load(Ordering::Acquire); |
| 53 | + let result = unsafe { crate::rand_r(&mut current_state as *mut _) }; |
| 54 | + RAND_STATE.store(current_state, Ordering::Release); |
| 55 | + result as _ |
| 56 | + } |
| 57 | + portable_atomic::cfg_has_atomic_cas! { with_cas() } |
| 58 | + portable_atomic::cfg_no_atomic_cas! { without_cas() } |
71 | 59 | }
|
72 | 60 |
|
73 | 61 | #[cfg(test)]
|
74 | 62 | mod test {
|
75 | 63 | use super::*;
|
76 | 64 | #[test]
|
77 | 65 | fn test_rand() {
|
78 |
| - unsafe { |
79 |
| - // Values taken from glibc implementation |
80 |
| - assert_eq!(rand(), 1804289383); |
81 |
| - assert_eq!(rand(), 846930886); |
82 |
| - assert_eq!(rand(), 1681692777); |
83 |
| - } |
84 |
| - } |
85 |
| - #[test] |
86 |
| - fn test_srand() { |
87 |
| - unsafe { |
88 |
| - srand(5); |
89 |
| - // Values taken from glibc implementation |
90 |
| - assert_eq!(rand(), 590011675); |
91 |
| - assert_eq!(rand(), 99788765); |
92 |
| - assert_eq!(rand(), 2131925610); |
| 66 | + assert_eq!(rand(), 1012483); |
| 67 | + assert_eq!(rand(), 1716955678); |
| 68 | + assert_eq!(rand(), 1792309081); |
| 69 | + srand(5); |
| 70 | + assert_eq!(rand(), 234104183); |
| 71 | + assert_eq!(rand(), 1214203243); |
| 72 | + assert_eq!(rand(), 1803669307); |
93 | 73 | }
|
94 | 74 | }
|
95 |
| -} |
0 commit comments