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

ThreadRng constructor with alternate seed source #1370

Open
Pr0methean opened this issue Jan 8, 2024 · 0 comments
Open

ThreadRng constructor with alternate seed source #1370

Pr0methean opened this issue Jan 8, 2024 · 0 comments

Comments

@Pr0methean
Copy link

Pr0methean commented Jan 8, 2024

Background

What is your motivation?

To develop a drop-in replacement for rand::thread_rng() that works around #1357 by using a buffering wrapper around OsRng where the buffer is a channel shared across all threads.

What type of application is this? (E.g. cryptography, game, numerical simulation)

A library intended for multithreaded Monte Carlo simulations.

Feature request

At https://github.com/Pr0methean/shared_buffer_rng/blob/85cf1caf6ffbb7018ff6d4464f4fc7b07d34b3d6/src/lib.rs#L110, to create an otherwise-identical drop-in replacement for rand::thread_rng() that was to differ only in how it obtained seeds, I had to copy many of ThreadRng's implementation details:

#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct ThreadLocalSeeder<const WORDS_PER_SEED: usize, const SEEDS_CAPACITY: usize, SourceType>
(Rc<UnsafeCell<BlockRng64<SharedBufferRng<WORDS_PER_SEED, SEEDS_CAPACITY, SourceType>>>>);

impl <const WORDS_PER_SEED: usize, const SEEDS_CAPACITY: usize, SourceType>
ThreadLocalSeeder<WORDS_PER_SEED, SEEDS_CAPACITY, SourceType> {
    fn new(source: SharedBufferRng<WORDS_PER_SEED, SEEDS_CAPACITY, SourceType>) -> Self {
        ThreadLocalSeeder(Rc::new(UnsafeCell::new(BlockRng64::new(source))))
    }

    fn get_mut(&self) -> &mut BlockRng64<SharedBufferRng<WORDS_PER_SEED, SEEDS_CAPACITY, SourceType>> {
        // SAFETY: Same as impl RngCore for ThreadRng: https://rust-random.github.io/rand/src/rand/rngs/thread.rs.html
        unsafe {
            self.0.get().as_mut().unwrap()
        }
    }
}

thread_local! {
    static DEFAULT_FOR_THREAD: ThreadLocalSeeder<8, 16, OsRng>
        = ThreadLocalSeeder::new(DEFAULT_ROOT.get_or_init(|| SharedBufferRngStd::new(OsRng::default())).clone());
}


impl <const WORDS_PER_SEED: usize, const SEEDS_CAPACITY: usize, SourceType>
RngCore for ThreadLocalSeeder<WORDS_PER_SEED, SEEDS_CAPACITY, SourceType> {
    fn next_u32(&mut self) -> u32 {
        self.get_mut().next_u32()
    }

    fn next_u64(&mut self) -> u64 {
        self.get_mut().next_u64()
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.get_mut().fill_bytes(dest)
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        self.get_mut().try_fill_bytes(dest)
    }
}

pub fn thread_seeder() -> ThreadLocalSeeder<8, 16, OsRng> {
    DEFAULT_FOR_THREAD.with(ThreadLocalSeeder::clone)
}

pub fn thread_rng() -> ReseedingRng<ChaCha12Core, ThreadLocalSeeder<8, 16, OsRng>> {
    let mut reseeder = thread_seeder();
    let mut seed = <ChaCha12Core as SeedableRng>::Seed::default();
    reseeder.fill_bytes(&mut seed);
    ReseedingRng::new(ChaCha12Core::from_seed(seed), 1 << 16, reseeder)
}

This is not future-proof against any change to the default algorithm, the reseeding threshold, or the use of Rc<UnsafeCell<_>>. It would be better if there was a method that would construct a ThreadRng given reseeder as the only parameter, and otherwise promise to have the same implementation details as thread_rng().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant