Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An in-memory RNG that shares its file descriptor. #14351

Merged
merged 2 commits into from Jan 5, 2017
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Next

An in-memory RNG that shares its file descriptor.

  • Loading branch information
asajeffrey committed Jan 5, 2017
commit 7ace30f9bd1dd835ddb1b12f788773cef855dae7

Some generated files are not rendered by default. Learn more.

@@ -14,7 +14,7 @@ bitflags = "0.7"
bluetooth_traits = {path = "../bluetooth_traits"}
device = {git = "https://github.com/servo/devices", features = ["bluetooth-test"]}
ipc-channel = "0.5"
rand = "0.3"
servo_rand = {path = "../rand"}
uuid = {version = "0.3.1", features = ["v4"]}

[target.'cfg(target_os = "linux")'.dependencies]
@@ -7,7 +7,7 @@ extern crate bitflags;
extern crate bluetooth_traits;
extern crate device;
extern crate ipc_channel;
extern crate rand;
extern crate servo_rand;
#[cfg(target_os = "linux")]
extern crate tinyfiledialogs;
extern crate uuid;
@@ -22,7 +22,7 @@ use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSeque
use device::bluetooth::{BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic};
use device::bluetooth::{BluetoothGATTDescriptor, BluetoothGATTService};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use rand::Rng;
use servo_rand::Rng;
use std::borrow::ToOwned;
use std::collections::{HashMap, HashSet};
use std::string::String;
@@ -397,7 +397,7 @@ impl BluetoothManager {

fn generate_device_id(&mut self) -> String {
let mut device_id;
let mut rng = rand::thread_rng();
let mut rng = servo_rand::thread_rng();
loop {
device_id = rng.gen::<u32>().to_string();
if !self.cached_devices.contains_key(&device_id) {
@@ -28,12 +28,12 @@ net_traits = {path = "../net_traits"}
offscreen_gl_context = "0.5.0"
plugins = {path = "../plugins"}
profile_traits = {path = "../profile_traits"}
rand = "0.3"
script_traits = {path = "../script_traits"}
serde = "0.8"
serde_derive = "0.8"
style_traits = {path = "../style_traits"}
servo_config = {path = "../config", features = ["servo"]}
servo_rand = {path = "../rand"}
servo_remutex = {path = "../remutex"}
servo_url = {path = "../url", features = ["servo"]}

@@ -93,7 +93,6 @@ use offscreen_gl_context::{GLContextAttributes, GLLimits};
use pipeline::{InitialPipelineState, Pipeline};
use profile_traits::mem;
use profile_traits::time;
use rand::{Rng, SeedableRng, StdRng, random};
use script_traits::{AnimationState, AnimationTickType, CompositorEvent};
use script_traits::{ConstellationControlMsg, ConstellationMsg as FromCompositorMsg};
use script_traits::{DocumentState, LayoutControlMsg, LoadData};
@@ -104,6 +103,7 @@ use script_traits::{MozBrowserErrorType, MozBrowserEvent, WebDriverCommandMsg, W
use script_traits::{SWManagerMsg, ScopeThings, WindowSizeType};
use servo_config::opts;
use servo_config::prefs::PREFS;
use servo_rand::{Rng, SeedableRng, ServoRng, random};
use servo_remutex::ReentrantMutex;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
@@ -276,7 +276,7 @@ pub struct Constellation<Message, LTF, STF> {

/// The random number generator and probability for closing pipelines.
/// This is for testing the hardening of the constellation.
random_pipeline_closure: Option<(StdRng, f32)>,
random_pipeline_closure: Option<(ServoRng, f32)>,

/// Phantom data that keeps the Rust type system happy.
phantom: PhantomData<(Message, LTF, STF)>,
@@ -530,7 +530,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
handled_warnings: VecDeque::new(),
random_pipeline_closure: opts::get().random_pipeline_closure_probability.map(|prob| {
let seed = opts::get().random_pipeline_closure_seed.unwrap_or_else(random);
let rng = StdRng::from_seed(&[seed]);
let rng = ServoRng::from_seed(&[seed]);
warn!("Randomly closing pipelines.");
info!("Using seed {} for random pipeline closure.", seed);
(rng, prob)
@@ -32,12 +32,12 @@ extern crate net_traits;
extern crate offscreen_gl_context;
#[macro_use]
extern crate profile_traits;
extern crate rand;
extern crate script_traits;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate servo_config;
extern crate servo_rand;
extern crate servo_remutex;
extern crate servo_url;
extern crate style_traits;
@@ -0,0 +1,15 @@
[package]
name = "servo_rand"
version = "0.0.1"
authors = ["The Servo Project Developers"]
license = "MPL-2.0"
publish = false

[lib]
name = "servo_rand"
path = "lib.rs"

[dependencies]
lazy_static = "0.2"
log = "0.3"
rand = "0.3"
@@ -0,0 +1,158 @@
/* 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 http://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;
extern crate rand;

pub use rand::{Rand, Rng, SeedableRng};
#[cfg(target_pointer_width = "64")]
use rand::isaac::Isaac64Rng as IsaacWordRng;
#[cfg(target_pointer_width = "32")]
use rand::isaac::IsaacRng as IsaacWordRng;
use rand::os::OsRng;
use rand::reseeding::{ReseedingRng, Reseeder};
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;
use std::sync::Mutex;
use std::u64;

// Slightly annoying having to cast between sizes.

#[cfg(target_pointer_width = "64")]
fn as_isaac_seed(seed: &[usize]) -> &[u64] {
unsafe { mem::transmute(seed) }
}

#[cfg(target_pointer_width = "32")]
fn as_isaac_seed(seed: &[usize]) -> &[u32] {
unsafe { mem::transmute(seed) }
}

// 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<IsaacWordRng, ServoReseeder>,
}

impl Rng for ServoRng {
#[inline]
fn next_u32(&mut self) -> u32 {
self.rng.next_u32()
}

#[inline]
fn next_u64(&mut self) -> u64 {
self.rng.next_u64()
}
}

impl<'a> SeedableRng<&'a [usize]> for ServoRng {
/// Create a manually-reseeding instane of `ServoRng`.
///
/// Note that this RNG does not reseed itself, so care is needed to reseed the RNG
/// is required to be cryptographically sound.
fn from_seed(seed: &[usize]) -> ServoRng {
debug!("Creating new manually-reseeded ServoRng.");
let isaac_rng = IsaacWordRng::from_seed(as_isaac_seed(seed));
let reseeding_rng = ReseedingRng::new(isaac_rng, u64::MAX, ServoReseeder);
ServoRng { rng: reseeding_rng }
}
/// Reseed the RNG.
fn reseed(&mut self, seed: &'a [usize]) {
debug!("Manually reseeding ServoRng.");
self.rng.reseed((ServoReseeder, as_isaac_seed(seed)))
}
}

impl ServoRng {
/// Create an auto-reseeding instance of `ServoRng`.
///
/// This uses the shared `OsRng`, so avoids consuming
/// a file descriptor.
pub fn new() -> ServoRng {
debug!("Creating new ServoRng.");
let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
let isaac_rng = IsaacWordRng::rand(&mut *os_rng);
let reseeding_rng = ReseedingRng::new(isaac_rng, RESEED_THRESHOLD, ServoReseeder);
ServoRng { rng: reseeding_rng }
}
}

// The reseeder for the in-memory RNG.
struct ServoReseeder;

impl Reseeder<IsaacWordRng> for ServoReseeder {
fn reseed(&mut self, rng: &mut IsaacWordRng) {
debug!("Reseeding ServoRng.");
let mut os_rng = OS_RNG.lock().expect("Poisoned lock.");
*rng = IsaacWordRng::rand(&mut *os_rng);
}
}

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 Rng 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)
}
}

// Generates a random value using the thread-local random number generator.
// A drop-in replacement for rand::random.
#[inline]
pub fn random<T: Rand>() -> T {
thread_rng().gen()
}
@@ -60,7 +60,6 @@ phf = "0.7.18"
phf_macros = "0.7.18"
plugins = {path = "../plugins"}
profile_traits = {path = "../profile_traits"}
rand = "0.3"
range = {path = "../range"}
ref_filter_map = "1.0.1"
ref_slice = "1.0"
@@ -73,6 +72,7 @@ serde = "0.8"
servo_atoms = {path = "../atoms"}
servo_config = {path = "../config", features = ["servo"] }
servo_geometry = {path = "../geometry" }
servo_rand = {path = "../rand"}
servo_url = {path = "../url", features = ["servo"] }
smallvec = "0.1"
style = {path = "../style"}
@@ -12,23 +12,23 @@ use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::globalscope::GlobalScope;
use js::jsapi::{JSContext, JSObject};
use js::jsapi::{JS_GetArrayBufferViewType, Type};
use rand::{OsRng, Rng};
use servo_rand::{ServoRng, Rng};

unsafe_no_jsmanaged_fields!(OsRng);
unsafe_no_jsmanaged_fields!(ServoRng);

// https://developer.mozilla.org/en-US/docs/Web/API/Crypto
#[dom_struct]
pub struct Crypto {
reflector_: Reflector,
#[ignore_heap_size_of = "Defined in rand"]
rng: DOMRefCell<OsRng>,
rng: DOMRefCell<ServoRng>,
}

impl Crypto {
fn new_inherited() -> Crypto {
Crypto {
reflector_: Reflector::new(),
rng: DOMRefCell::new(OsRng::new().unwrap()),
rng: DOMRefCell::new(ServoRng::new()),
}
}

@@ -29,10 +29,10 @@ use js::rust::Runtime;
use msg::constellation_msg::FrameId;
use net_traits::{IpcSend, load_whole_resource};
use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType};
use rand::random;
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
use script_runtime::ScriptThreadEventCategory::WorkerEvent;
use script_traits::{TimerEvent, TimerSource, WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
use servo_rand::random;
use servo_url::ServoUrl;
use std::mem::replace;
use std::sync::{Arc, Mutex};
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.