servo_rand/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::RefCell;
6use std::rc::Rc;
7use std::sync::Mutex;
8
9use log::trace;
10use malloc_size_of_derive::MallocSizeOf;
11/// A random number generator which shares one instance of an `OsRng`.
12///
13/// A problem with `OsRng`, which is inherited by `StdRng` and so
14/// `ThreadRng`, is that it reads from `/dev/random`, and so consumes
15/// a file descriptor. For multi-threaded applications like Servo,
16/// it is easy to exhaust the supply of file descriptors this way.
17///
18/// This crate fixes that, by only using one `OsRng`, which is just
19/// used to seed and re-seed an `ServoRng`.
20use rand::distributions::{Distribution, Standard};
21use rand::rngs::OsRng;
22use rand::rngs::adapter::ReseedingRng;
23pub use rand::seq::SliceRandom;
24pub use rand::{Rng, RngCore, SeedableRng};
25use rand_isaac::isaac::IsaacCore;
26use uuid::{Builder, Uuid};
27
28// The shared RNG which may hold on to a file descriptor
29static OS_RNG: Mutex<OsRng> = Mutex::new(OsRng);
30
31// Generate 32K of data between reseedings
32const RESEED_THRESHOLD: u64 = 32_768;
33
34// An in-memory RNG that only uses the shared file descriptor for seeding and reseeding.
35#[derive(MallocSizeOf)]
36pub struct ServoRng {
37    #[ignore_malloc_size_of = "Defined in rand"]
38    rng: ReseedingRng<IsaacCore, ServoReseeder>,
39}
40
41impl RngCore for ServoRng {
42    #[inline]
43    fn next_u32(&mut self) -> u32 {
44        self.rng.next_u32()
45    }
46
47    #[inline]
48    fn next_u64(&mut self) -> u64 {
49        self.rng.next_u64()
50    }
51
52    #[inline]
53    fn fill_bytes(&mut self, bytes: &mut [u8]) {
54        self.rng.fill_bytes(bytes)
55    }
56
57    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
58        self.rng.try_fill_bytes(bytes)
59    }
60}
61
62#[derive(Default)]
63pub struct Seed([u8; 32]);
64
65impl AsMut<[u8]> for Seed {
66    fn as_mut(&mut self) -> &mut [u8] {
67        &mut self.0
68    }
69}
70
71impl SeedableRng for ServoRng {
72    type Seed = Seed;
73
74    // This function is used in the reseeding process of rand hence why the RESEED_THRESHOLD is
75    // used.
76    fn from_seed(seed: Seed) -> ServoRng {
77        trace!("Creating a new ServoRng.");
78        let isaac_rng = IsaacCore::from_seed(seed.0);
79        let reseeding_rng = ReseedingRng::new(isaac_rng, RESEED_THRESHOLD, ServoReseeder);
80        ServoRng { rng: reseeding_rng }
81    }
82}
83
84impl ServoRng {
85    /// Create a manually-reseeding instance of `ServoRng`.
86    ///
87    /// Note that this RNG does not reseed itself, so care is needed to reseed the RNG
88    /// is required to be cryptographically sound.
89    pub fn new_manually_reseeded(seed: u64) -> ServoRng {
90        trace!("Creating a new manually-reseeded ServoRng.");
91        let isaac_rng = IsaacCore::seed_from_u64(seed);
92        let reseeding_rng = ReseedingRng::new(isaac_rng, 0, ServoReseeder);
93        ServoRng { rng: reseeding_rng }
94    }
95}
96
97impl Default for ServoRng {
98    /// Create an auto-reseeding instance of `ServoRng`.
99    ///
100    /// This uses the shared `OsRng`, so avoids consuming
101    /// a file descriptor.
102    fn default() -> Self {
103        trace!("Creating new ServoRng.");
104        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
105        let isaac_rng = IsaacCore::from_rng(&mut *os_rng).unwrap();
106        let reseeding_rng = ReseedingRng::new(isaac_rng, RESEED_THRESHOLD, ServoReseeder);
107        ServoRng { rng: reseeding_rng }
108    }
109}
110
111// The reseeder for the in-memory RNG.
112struct ServoReseeder;
113
114impl RngCore for ServoReseeder {
115    #[inline]
116    fn next_u32(&mut self) -> u32 {
117        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
118        os_rng.next_u32()
119    }
120
121    #[inline]
122    fn next_u64(&mut self) -> u64 {
123        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
124        os_rng.next_u64()
125    }
126
127    #[inline]
128    fn fill_bytes(&mut self, bytes: &mut [u8]) {
129        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
130        os_rng.fill_bytes(bytes)
131    }
132
133    #[inline]
134    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
135        let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
136        os_rng.try_fill_bytes(bytes)
137    }
138}
139
140impl Default for ServoReseeder {
141    fn default() -> ServoReseeder {
142        ServoReseeder
143    }
144}
145
146// A thread-local RNG, designed as a drop-in replacement for rand::ThreadRng.
147#[derive(Clone)]
148pub struct ServoThreadRng {
149    rng: Rc<RefCell<ServoRng>>,
150}
151
152// A thread-local RNG, designed as a drop-in replacement for rand::thread_rng.
153pub fn thread_rng() -> ServoThreadRng {
154    SERVO_THREAD_RNG.with(|t| t.clone())
155}
156
157thread_local! {
158    static SERVO_THREAD_RNG: ServoThreadRng = ServoThreadRng { rng: Rc::new(RefCell::new(ServoRng::default())) };
159}
160
161impl RngCore for ServoThreadRng {
162    fn next_u32(&mut self) -> u32 {
163        self.rng.borrow_mut().next_u32()
164    }
165
166    fn next_u64(&mut self) -> u64 {
167        self.rng.borrow_mut().next_u64()
168    }
169
170    #[inline]
171    fn fill_bytes(&mut self, bytes: &mut [u8]) {
172        self.rng.borrow_mut().fill_bytes(bytes)
173    }
174
175    #[inline]
176    fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
177        (self.rng.borrow_mut()).try_fill_bytes(bytes)
178    }
179}
180
181// Generates a random value using the thread-local random number generator.
182// A drop-in replacement for rand::random.
183#[inline]
184pub fn random<T>() -> T
185where
186    Standard: Distribution<T>,
187{
188    thread_rng().r#gen()
189}
190
191// TODO(eijebong): Replace calls to this by random once `uuid::Uuid` implements `rand::Rand` again.
192#[inline]
193pub fn random_uuid() -> Uuid {
194    let mut bytes = [0; 16];
195    thread_rng().fill_bytes(&mut bytes);
196    Builder::from_random_bytes(bytes).into_uuid()
197}