mirror of
https://github.com/servo/servo.git
synced 2025-06-14 11:24:33 +00:00
I changed the isaac random to always be the 32 bits one because the 64 bits version doesn't work with ReseedingRng as it requires the generator's output to be u32 for some reasons. The other main change is that the reseed method doesn't exist anymore and now rand recreates a new generator when it needs to reseed so I had to add a new method for when we don't want a generator to be reseeded. Closes #23558
212 lines
5.9 KiB
Rust
212 lines
5.9 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
/// A random number generator which shares one instance of an `OsRng`.
|
|
///
|
|
/// A problem with `OsRng`, which is inherited by `StdRng` and so
|
|
/// `ThreadRng`, is that it reads from `/dev/random`, and so consumes
|
|
/// a file descriptor. For multi-threaded applications like Servo,
|
|
/// it is easy to exhaust the supply of file descriptors this way.
|
|
///
|
|
/// This crate fixes that, by only using one `OsRng`, which is just
|
|
/// used to seed and re-seed an `ServoRng`.
|
|
|
|
#[macro_use]
|
|
extern crate lazy_static;
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
use rand::distributions::{Distribution, Standard};
|
|
use rand::rngs::adapter::ReseedingRng;
|
|
use rand::rngs::OsRng;
|
|
pub use rand::seq::SliceRandom;
|
|
pub use rand::{Rng, RngCore, SeedableRng};
|
|
use rand_isaac::isaac::IsaacCore;
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
use std::sync::Mutex;
|
|
use std::u64;
|
|
use uuid::{Builder, Uuid, Variant, Version};
|
|
|
|
// The shared RNG which may hold on to a file descriptor
|
|
lazy_static! {
|
|
static ref OS_RNG: Mutex<OsRng> = match OsRng::new() {
|
|
Ok(r) => Mutex::new(r),
|
|
Err(e) => panic!("Failed to seed OsRng: {}", e),
|
|
};
|
|
}
|
|
|
|
// Generate 32K of data between reseedings
|
|
const RESEED_THRESHOLD: u64 = 32_768;
|
|
|
|
// An in-memory RNG that only uses the shared file descriptor for seeding and reseeding.
|
|
pub struct ServoRng {
|
|
rng: ReseedingRng<IsaacCore, ServoReseeder>,
|
|
}
|
|
|
|
impl RngCore for ServoRng {
|
|
#[inline]
|
|
fn next_u32(&mut self) -> u32 {
|
|
self.rng.next_u32()
|
|
}
|
|
|
|
#[inline]
|
|
fn next_u64(&mut self) -> u64 {
|
|
self.rng.next_u64()
|
|
}
|
|
|
|
#[inline]
|
|
fn fill_bytes(&mut self, bytes: &mut [u8]) {
|
|
self.rng.fill_bytes(bytes)
|
|
}
|
|
|
|
fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
|
|
self.rng.try_fill_bytes(bytes)
|
|
}
|
|
}
|
|
|
|
pub struct Seed([u8; 32]);
|
|
|
|
impl Default for Seed {
|
|
fn default() -> Self {
|
|
Seed([0; 32])
|
|
}
|
|
}
|
|
|
|
impl AsMut<[u8]> for Seed {
|
|
fn as_mut(&mut self) -> &mut [u8] {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl SeedableRng for ServoRng {
|
|
type Seed = Seed;
|
|
|
|
// This function is used in the reseeding process of rand hence why the RESEED_THRESHOLD is
|
|
// used.
|
|
fn from_seed(seed: Seed) -> ServoRng {
|
|
trace!("Creating a new ServoRng.");
|
|
let isaac_rng = IsaacCore::from_seed(seed.0);
|
|
let reseeding_rng = ReseedingRng::new(isaac_rng, RESEED_THRESHOLD, ServoReseeder);
|
|
ServoRng { rng: reseeding_rng }
|
|
}
|
|
}
|
|
|
|
impl ServoRng {
|
|
/// Create a manually-reseeding instance of `ServoRng`.
|
|
///
|
|
/// Note that this RNG does not reseed itself, so care is needed to reseed the RNG
|
|
/// is required to be cryptographically sound.
|
|
pub fn new_manually_reseeded(seed: u64) -> ServoRng {
|
|
trace!("Creating a new manually-reseeded ServoRng.");
|
|
let isaac_rng = IsaacCore::seed_from_u64(seed);
|
|
let reseeding_rng = ReseedingRng::new(isaac_rng, u64::MAX, ServoReseeder);
|
|
ServoRng { rng: reseeding_rng }
|
|
}
|
|
}
|
|
|
|
impl ServoRng {
|
|
/// Create an auto-reseeding instance of `ServoRng`.
|
|
///
|
|
/// This uses the shared `OsRng`, so avoids consuming
|
|
/// a file descriptor.
|
|
pub fn new() -> ServoRng {
|
|
trace!("Creating new ServoRng.");
|
|
let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
|
|
let isaac_rng = IsaacCore::from_rng(&mut *os_rng).unwrap();
|
|
let reseeding_rng = ReseedingRng::new(isaac_rng, RESEED_THRESHOLD, ServoReseeder);
|
|
ServoRng { rng: reseeding_rng }
|
|
}
|
|
}
|
|
|
|
// The reseeder for the in-memory RNG.
|
|
struct ServoReseeder;
|
|
|
|
impl RngCore for ServoReseeder {
|
|
#[inline]
|
|
fn next_u32(&mut self) -> u32 {
|
|
let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
|
|
os_rng.next_u32()
|
|
}
|
|
|
|
#[inline]
|
|
fn next_u64(&mut self) -> u64 {
|
|
let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
|
|
os_rng.next_u64()
|
|
}
|
|
|
|
#[inline]
|
|
fn fill_bytes(&mut self, bytes: &mut [u8]) {
|
|
let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
|
|
os_rng.fill_bytes(bytes)
|
|
}
|
|
|
|
#[inline]
|
|
fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
|
|
let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
|
|
os_rng.try_fill_bytes(bytes)
|
|
}
|
|
}
|
|
|
|
impl Default for ServoReseeder {
|
|
fn default() -> ServoReseeder {
|
|
ServoReseeder
|
|
}
|
|
}
|
|
|
|
// A thread-local RNG, designed as a drop-in replacement for rand::ThreadRng.
|
|
#[derive(Clone)]
|
|
pub struct ServoThreadRng {
|
|
rng: Rc<RefCell<ServoRng>>,
|
|
}
|
|
|
|
// A thread-local RNG, designed as a drop-in replacement for rand::thread_rng.
|
|
pub fn thread_rng() -> ServoThreadRng {
|
|
SERVO_THREAD_RNG.with(|t| t.clone())
|
|
}
|
|
|
|
thread_local! {
|
|
static SERVO_THREAD_RNG: ServoThreadRng = ServoThreadRng { rng: Rc::new(RefCell::new(ServoRng::new())) };
|
|
}
|
|
|
|
impl RngCore for ServoThreadRng {
|
|
fn next_u32(&mut self) -> u32 {
|
|
self.rng.borrow_mut().next_u32()
|
|
}
|
|
|
|
fn next_u64(&mut self) -> u64 {
|
|
self.rng.borrow_mut().next_u64()
|
|
}
|
|
|
|
#[inline]
|
|
fn fill_bytes(&mut self, bytes: &mut [u8]) {
|
|
self.rng.borrow_mut().fill_bytes(bytes)
|
|
}
|
|
|
|
#[inline]
|
|
fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> std::result::Result<(), rand_core::Error> {
|
|
(self.rng.borrow_mut()).try_fill_bytes(bytes)
|
|
}
|
|
}
|
|
|
|
// Generates a random value using the thread-local random number generator.
|
|
// A drop-in replacement for rand::random.
|
|
#[inline]
|
|
pub fn random<T>() -> T
|
|
where
|
|
Standard: Distribution<T>,
|
|
{
|
|
thread_rng().gen()
|
|
}
|
|
|
|
// TODO(eijebong): Replace calls to this by random once `uuid::Uuid` implements `rand::Rand` again.
|
|
#[inline]
|
|
pub fn random_uuid() -> Uuid {
|
|
let mut bytes = [0; 16];
|
|
thread_rng().fill_bytes(&mut bytes);
|
|
Builder::from_bytes(bytes)
|
|
.set_variant(Variant::RFC4122)
|
|
.set_version(Version::Random)
|
|
.build()
|
|
}
|