Make PyRandom ThreadSafe

This commit is contained in:
Aviv Palivoda committed 2020-05-15 10:31:38 +03:00
1 parent 4ca4709d5a
commit 0cea276cb1
3 files changed
+65 -22

No files matched your search

Generated
+1
View File
@@ -1575,6 +1575,7 @@ dependencies = [
"flamer",
"flate2",
"foreign-types-shared",
"generational-arena",
"gethostname",
"getrandom",
"hex",
+1
View File
@@ -70,6 +70,7 @@ num_enum = "0.4"
smallbox = "0.8"
bstr = "0.2.12"
crossbeam-utils = "0.7"
generational-arena = "0.2"
## unicode stuff
unicode_names2 = "0.4"
+63 -22
View File
@@ -7,8 +7,9 @@ mod _random {
use crate::function::OptionalOption;
use crate::obj::objint::PyIntRef;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyClassImpl, PyRef, PyResult, PyValue};
use crate::pyobject::{PyClassImpl, PyRef, PyResult, PyValue, ThreadSafe};
use crate::VirtualMachine;
use generational_arena::{self, Arena};
use num_bigint::{BigInt, Sign};
use num_traits::Signed;
use rand::RngCore;
@@ -53,12 +54,51 @@ mod _random {
}
}
thread_local!(static RNG_HANDLES: RefCell<Arena<PyRng>> = RefCell::new(Arena::new()));
#[derive(Debug)]
struct RngHandle(generational_arena::Index);
impl RngHandle {
fn new(rng: PyRng) -> Self {
let idx = RNG_HANDLES.with(|arena| arena.borrow_mut().insert(rng));
RngHandle(idx)
}
fn exec<F, R>(&self, func: F) -> R
where
F: Fn(&mut PyRng) -> R,
{
RNG_HANDLES.with(|arena| {
func(
arena
.borrow_mut()
.get_mut(self.0)
.expect("index was removed"),
)
})
}
fn replace(&self, rng: PyRng) {
RNG_HANDLES.with(|arena| {
*arena
.borrow_mut()
.get_mut(self.0)
.expect("index was removed") = rng
})
}
}
impl Drop for RngHandle {
fn drop(&mut self) {
RNG_HANDLES.with(|arena| arena.borrow_mut().remove(self.0));
}
}
#[pyclass(name = "Random")]
#[derive(Debug)]
struct PyRandom {
rng: RefCell<PyRng>,
rng: RngHandle,
}
impl ThreadSafe for PyRandom {}
impl PyValue for PyRandom {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("_random", "Random")
@@ -70,14 +110,14 @@ mod _random {
#[pyslot(new)]
fn new(cls: PyClassRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
PyRandom {
rng: RefCell::new(PyRng::default()),
rng: RngHandle::new(PyRng::default()),
}
.into_ref_with_type(vm, cls)
}
#[pymethod]
fn random(&self) -> f64 {
mt19937::gen_res53(&mut *self.rng.borrow_mut())
self.rng.exec(mt19937::gen_res53)
}
#[pymethod]
@@ -93,31 +133,32 @@ mod _random {
}
};
*self.rng.borrow_mut() = new_rng;
self.rng.replace(new_rng);
}
#[pymethod]
fn getrandbits(&self, mut k: usize) -> BigInt {
let mut rng = self.rng.borrow_mut();
fn getrandbits(&self, k: usize) -> BigInt {
self.rng.exec(|rng| {
let mut k = k;
let mut gen_u32 = |k| rng.next_u32() >> (32 - k) as u32;
let mut gen_u32 = |k| rng.next_u32() >> (32 - k) as u32;
if k <= 32 {
return gen_u32(k).into();
}
if k <= 32 {
return gen_u32(k).into();
}
let words = (k - 1) / 8 + 1;
let mut wordarray = vec![0u32; words];
let words = (k - 1) / 8 + 1;
let mut wordarray = vec![0u32; words];
let it = wordarray.iter_mut();
#[cfg(target_endian = "big")]
let it = it.rev();
for word in it {
*word = gen_u32(k);
k -= 32;
}
let it = wordarray.iter_mut();
#[cfg(target_endian = "big")]
let it = it.rev();
for word in it {
*word = gen_u32(k);
k -= 32;
}
BigInt::from_slice(Sign::NoSign, &wordarray)
BigInt::from_slice(Sign::NoSign, &wordarray)
})
}
}
}