From 57c850489951a107e851503e0e0b0b7688a4c68e Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 11 May 2018 17:26:23 -0700 Subject: [PATCH] proxy: Add a lazy version of ThreadRng (#936) This is in preparation for landing the Tokio upgrade. In order to be generic over Tokio's current thread and threadpool executors, a number of types in Conduit which were not previously `Send` are now required to be `Send`. A majority of this work will be done in the main Tokio upgrade PR, as it is in many cases not possible to make these types `Send` _without_ using the new Tokio API (in order to remove `Handle`s, etc.); however, I'm factoring out everything possible and trying to land it in separate PRs. The p2c load balancer constructed in `Outbound` is currently parameterized over a random number generator. We currently construct it by getting the thread-local RNG, and passing it to the load balancer constructor. However, the thread-local RNG is not `Send`. I've fixed this issue by creating a new zero-sized empty struct type which implements `rand::Rng` simply by calling `thread_rng()` every time its' called, and passing that to `choose::power_of_two_choices` instead. Since this is an empty type which contains no data, and the correct thread-local RNG is accessed whenever the methods are called, this new type can trivially be `Send`. According to the `rand` crate's documentation, this is the correct way to use `ThreadRng` anyway: > Retrieve the lazily-initialized thread-local random number generator, seeded > by the system. Intended to be used in method chaining style, e.g. > `thread_rng().gen::()`. > (from https://docs.rs/rand/0.4.2/rand/fn.thread_rng.html) This shouldn't lead to any functional changes. Signed-off-by: Eliza Weisman --- proxy/src/lib.rs | 1 + proxy/src/outbound.rs | 6 +++--- proxy/src/rng.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 proxy/src/rng.rs diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs index bc3b09f01..c79d6e589 100644 --- a/proxy/src/lib.rs +++ b/proxy/src/lib.rs @@ -76,6 +76,7 @@ mod transparency; mod transport; pub mod timeout; mod tower_fn; // TODO: move to tower-fn +mod rng; use bind::Bind; use connection::BoundPort; diff --git a/proxy/src/outbound.rs b/proxy/src/outbound.rs index cf09175df..f4cc67346 100644 --- a/proxy/src/outbound.rs +++ b/proxy/src/outbound.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use http; use futures::{Async, Poll}; -use rand; use tower_service as tower; use tower_balance::{self, choose, load, Balance}; use tower_buffer::Buffer; @@ -21,6 +20,7 @@ use ctx; use timeout::Timeout; use transparency::h1; use transport::{DnsNameAndPort, Host, HostAndPort}; +use rng::LazyThreadRng; type BindProtocol = bind::BindProtocol, B>; @@ -77,7 +77,7 @@ where type RouteError = bind::BufferSpawnError; type Service = InFlightLimit>, - choose::PowerOfTwoChoices + choose::PowerOfTwoChoices >>>>; fn recognize(&self, req: &Self::Request) -> Option { @@ -153,7 +153,7 @@ where let loaded = tower_balance::load::WithPendingRequests::new(resolve); - let balance = tower_balance::power_of_two_choices(loaded, rand::thread_rng()); + let balance = tower_balance::power_of_two_choices(loaded, LazyThreadRng); // use the same executor as the underlying `Bind` for the `Buffer` and // `Timeout`. diff --git a/proxy/src/rng.rs b/proxy/src/rng.rs new file mode 100644 index 000000000..9f7920d66 --- /dev/null +++ b/proxy/src/rng.rs @@ -0,0 +1,26 @@ +use rand; + +/// An empty type which implements `rand::Rng` by lazily getting the current +/// `thread_rng` when its' called. +/// +/// This can be used in cases where we need a type to be `Send`, but wish to +/// use the thread-local RNG. +#[derive(Copy, Clone, Debug, Default)] +pub struct LazyThreadRng; + +// ===== impl LazyRng ===== + +impl rand::Rng for LazyThreadRng { + fn next_u32(&mut self) -> u32 { + rand::thread_rng().next_u32() + } + + fn next_u64(&mut self) -> u64 { + rand::thread_rng().next_u64() + } + + #[inline] + fn fill_bytes(&mut self, bytes: &mut [u8]) { + rand::thread_rng().fill_bytes(bytes) + } +}