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::<i32>()`.
> (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 <eliza@buoyant.io>
This commit is contained in:
Eliza Weisman 2018-05-11 17:26:23 -07:00 committed by GitHub
parent 281281f5bc
commit 57c8504899
3 changed files with 30 additions and 3 deletions

View File

@ -76,6 +76,7 @@ mod transparency;
mod transport; mod transport;
pub mod timeout; pub mod timeout;
mod tower_fn; // TODO: move to tower-fn mod tower_fn; // TODO: move to tower-fn
mod rng;
use bind::Bind; use bind::Bind;
use connection::BoundPort; use connection::BoundPort;

View File

@ -5,7 +5,6 @@ use std::sync::Arc;
use http; use http;
use futures::{Async, Poll}; use futures::{Async, Poll};
use rand;
use tower_service as tower; use tower_service as tower;
use tower_balance::{self, choose, load, Balance}; use tower_balance::{self, choose, load, Balance};
use tower_buffer::Buffer; use tower_buffer::Buffer;
@ -21,6 +20,7 @@ use ctx;
use timeout::Timeout; use timeout::Timeout;
use transparency::h1; use transparency::h1;
use transport::{DnsNameAndPort, Host, HostAndPort}; use transport::{DnsNameAndPort, Host, HostAndPort};
use rng::LazyThreadRng;
type BindProtocol<B> = bind::BindProtocol<Arc<ctx::Proxy>, B>; type BindProtocol<B> = bind::BindProtocol<Arc<ctx::Proxy>, B>;
@ -77,7 +77,7 @@ where
type RouteError = bind::BufferSpawnError; type RouteError = bind::BufferSpawnError;
type Service = InFlightLimit<Timeout<Buffer<Balance< type Service = InFlightLimit<Timeout<Buffer<Balance<
load::WithPendingRequests<Discovery<B>>, load::WithPendingRequests<Discovery<B>>,
choose::PowerOfTwoChoices<rand::ThreadRng> choose::PowerOfTwoChoices<LazyThreadRng>
>>>>; >>>>;
fn recognize(&self, req: &Self::Request) -> Option<Self::Key> { fn recognize(&self, req: &Self::Request) -> Option<Self::Key> {
@ -153,7 +153,7 @@ where
let loaded = tower_balance::load::WithPendingRequests::new(resolve); 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 // use the same executor as the underlying `Bind` for the `Buffer` and
// `Timeout`. // `Timeout`.

26
proxy/src/rng.rs Normal file
View File

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