Skip to content

Commit e4c7274

Browse files
committed
Remove TLS by using sync::Once
1 parent 0a18857 commit e4c7274

9 files changed

+169
-202
lines changed

src/lib.rs

+5-18
Original file line numberDiff line numberDiff line change
@@ -128,24 +128,11 @@ extern crate log;
128128
#[allow(unused)]
129129
macro_rules! error { ($($x:tt)*) => () }
130130

131-
// temp fix for stdweb
132-
#[cfg(target_arch = "wasm32")]
133-
extern crate std;
134-
135-
#[cfg(any(
136-
target_os = "android",
137-
target_os = "netbsd",
138-
target_os = "solaris",
139-
target_os = "illumos",
140-
target_os = "redox",
141-
target_os = "dragonfly",
142-
target_os = "haiku",
143-
target_os = "linux",
144-
all(
145-
target_arch = "wasm32",
146-
not(target_os = "wasi")
147-
),
148-
))]
131+
#[cfg(any(unix, target_os = "redox", target_arch = "wasm32"))]
132+
#[allow(unused)]
133+
mod once;
134+
#[cfg(any(unix, target_os = "redox"))]
135+
#[allow(unused)]
149136
mod utils;
150137
mod error;
151138
pub use crate::error::Error;

src/linux_android.rs

+15-48
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,16 @@
99
//! Implementation for Linux / Android
1010
extern crate std;
1111

12-
use crate::Error;
13-
use crate::utils::use_init;
14-
use std::{thread_local, io::{self, Read}, fs::File};
15-
use core::cell::RefCell;
12+
use crate::{once::Once, utils::use_file, Error};
1613
use core::num::NonZeroU32;
17-
use core::sync::atomic::{AtomicBool, Ordering};
18-
19-
static RNG_INIT: AtomicBool = AtomicBool::new(false);
14+
use std::io;
2015

2116
enum RngSource {
2217
GetRandom,
23-
Device(File),
18+
File,
2419
}
2520

26-
thread_local!(
27-
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
28-
);
21+
static RNG_SOURCE: Once<RngSource> = Once::new();
2922

3023
fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> {
3124
let ret = unsafe {
@@ -39,46 +32,20 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> {
3932
}
4033

4134
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
42-
RNG_SOURCE.with(|f| {
43-
use_init(f,
44-
|| {
45-
let s = if is_getrandom_available() {
46-
RngSource::GetRandom
47-
} else {
48-
// read one byte from "/dev/random" to ensure that
49-
// OS RNG has initialized
50-
if !RNG_INIT.load(Ordering::Relaxed) {
51-
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
52-
RNG_INIT.store(true, Ordering::Relaxed)
53-
}
54-
RngSource::Device(File::open("/dev/urandom")?)
55-
};
56-
Ok(s)
57-
}, |f| {
58-
match f {
59-
RngSource::GetRandom => syscall_getrandom(dest),
60-
RngSource::Device(f) => f.read_exact(dest),
61-
}.map_err(From::from)
62-
})
63-
})
64-
}
65-
66-
fn is_getrandom_available() -> bool {
67-
use std::sync::{Once, ONCE_INIT};
68-
69-
static CHECKER: Once = ONCE_INIT;
70-
static AVAILABLE: AtomicBool = AtomicBool::new(false);
71-
72-
CHECKER.call_once(|| {
35+
let source = RNG_SOURCE.call_once(|| {
7336
let mut buf: [u8; 0] = [];
74-
let available = match syscall_getrandom(&mut buf) {
75-
Ok(()) => true,
76-
Err(err) => err.raw_os_error() != Some(libc::ENOSYS),
77-
};
78-
AVAILABLE.store(available, Ordering::Relaxed);
37+
if let Err(err) = syscall_getrandom(&mut buf) {
38+
if err.raw_os_error() == Some(libc::ENOSYS) {
39+
return RngSource::File;
40+
}
41+
}
42+
RngSource::GetRandom
7943
});
8044

81-
AVAILABLE.load(Ordering::Relaxed)
45+
match source {
46+
RngSource::GetRandom => Ok(syscall_getrandom(dest)?),
47+
RngSource::File => use_file("/dev/urandom", None, dest),
48+
}
8249
}
8350

8451
#[inline(always)]

src/once.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2019 Developers of the Rand project.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
//
9+
// Based on code from the "spin" create (MIT licensed).
10+
extern crate std;
11+
12+
use core::{cell::UnsafeCell, ptr};
13+
use std::sync;
14+
15+
/// An std-based implementation of spin::Once.
16+
pub struct Once<T> {
17+
once: sync::Once,
18+
data: UnsafeCell<Option<T>>,
19+
}
20+
21+
unsafe impl<T: Send + Sync> Sync for Once<T> {}
22+
unsafe impl<T: Send> Send for Once<T> {}
23+
24+
impl<T> Once<T> {
25+
/// Creates a new `Once` value.
26+
pub const fn new() -> Self {
27+
Self {
28+
once: sync::Once::new(),
29+
data: UnsafeCell::new(None),
30+
}
31+
}
32+
33+
/// Performs an initialization routine once and only once. The given closure
34+
/// will be executed if this is the first time `call_once` has been called,
35+
/// and otherwise the routine will *not* be invoked.
36+
///
37+
/// This method will block the calling thread if another initialization
38+
/// routine is currently running.
39+
///
40+
/// When this function returns, it is guaranteed that some initialization
41+
/// has run and completed (it may not be the closure specified). The
42+
/// returned pointer will point to the result from the closure that was
43+
/// run.
44+
pub fn call_once<F>(&self, f: F) -> &T
45+
where
46+
F: FnOnce() -> T,
47+
{
48+
self.once
49+
.call_once(|| unsafe { ptr::write(self.data.get(), Some(f())) });
50+
unsafe { &*self.data.get() }.as_ref().unwrap()
51+
}
52+
}

src/solaris_illumos.rs

+24-50
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919
//! libc::dlsym.
2020
extern crate std;
2121

22-
use crate::Error;
23-
use crate::utils::use_init;
24-
use std::{thread_local, io::{self, Read}, fs::File};
25-
use core::cell::RefCell;
22+
use crate::{once::Once, utils::use_file, Error};
23+
use core::mem;
2624
use core::num::NonZeroU32;
25+
use std::io;
2726

2827
#[cfg(target_os = "illumos")]
2928
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
@@ -32,12 +31,10 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) ->
3231

3332
enum RngSource {
3433
GetRandom(GetRandomFn),
35-
Device(File),
34+
File,
3635
}
3736

38-
thread_local!(
39-
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
40-
);
37+
static RNG_SOURCE: Once<RngSource> = Once::new();
4138

4239
fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
4340
let ret = unsafe { rand(dest.as_mut_ptr(), dest.len(), 0) as libc::ssize_t };
@@ -51,51 +48,28 @@ fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
5148
}
5249

5350
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
54-
// 256 bytes is the lowest common denominator across all the Solaris
55-
// derived platforms for atomically obtaining random data.
56-
RNG_SOURCE.with(|f| {
57-
use_init(
58-
f,
59-
|| {
60-
let s = match fetch_getrandom() {
61-
Some(fptr) => RngSource::GetRandom(fptr),
62-
None => RngSource::Device(File::open("/dev/random")?),
63-
};
64-
Ok(s)
65-
},
66-
|f| {
67-
match f {
68-
RngSource::GetRandom(rp) => {
69-
for chunk in dest.chunks_mut(256) {
70-
libc_getrandom(*rp, chunk)?
71-
}
72-
}
73-
RngSource::Device(randf) => {
74-
for chunk in dest.chunks_mut(256) {
75-
randf.read_exact(chunk)?
76-
}
77-
}
78-
};
79-
Ok(())
80-
},
81-
)
82-
})
83-
}
84-
85-
fn fetch_getrandom() -> Option<GetRandomFn> {
86-
use std::mem;
87-
use std::sync::atomic::{AtomicUsize, Ordering};
51+
let source = RNG_SOURCE.call_once(|| {
52+
let name = "getrandom\0";
53+
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) };
8854

89-
static FPTR: AtomicUsize = AtomicUsize::new(1);
55+
if addr.is_null() {
56+
RngSource::File
57+
} else {
58+
RngSource::GetRandom(unsafe { mem::transmute(addr) })
59+
}
60+
});
9061

91-
if FPTR.load(Ordering::SeqCst) == 1 {
92-
let name = "getrandom\0";
93-
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize };
94-
FPTR.store(addr, Ordering::SeqCst);
62+
// 256 bytes is the lowest common denominator across all the Solaris
63+
// derived platforms for atomically obtaining random data.
64+
match source {
65+
RngSource::GetRandom(rp) => {
66+
for chunk in dest.chunks_mut(256) {
67+
libc_getrandom(*rp, chunk)?
68+
}
69+
Ok(())
70+
}
71+
RngSource::File => use_file("/dev/urandom", Some(256), dest),
9572
}
96-
97-
let ptr = FPTR.load(Ordering::SeqCst);
98-
unsafe { mem::transmute::<usize, Option<GetRandomFn>>(ptr) }
9973
}
10074

10175
#[inline(always)]

src/use_file.rs

+5-29
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,12 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
//! Implementation for DragonFly / Haiku
9+
//! Implementations that just need to read from a file
1010
extern crate std;
1111

12-
use crate::Error;
13-
use crate::utils::use_init;
14-
use std::{thread_local, io::Read, fs::File};
15-
use core::cell::RefCell;
12+
use crate::{utils::use_file, Error};
1613
use core::num::NonZeroU32;
1714

18-
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
19-
2015
#[cfg(target_os = "redox")]
2116
const FILE_PATH: &str = "rand:";
2217
#[cfg(target_os = "netbsd")]
@@ -25,31 +20,12 @@ const FILE_PATH: &str = "/dev/urandom";
2520
const FILE_PATH: &str = "/dev/random";
2621

2722
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
28-
RNG_FILE.with(|f| {
29-
use_init(f, || init_file(), |f| use_file(f, dest))
30-
})
31-
}
32-
33-
fn use_file(f: &mut File, dest: &mut [u8]) -> Result<(), Error> {
23+
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
3424
if cfg!(target_os = "emscripten") {
35-
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
36-
for chunk in dest.chunks_mut(65536) {
37-
f.read_exact(chunk)?;
38-
}
25+
use_file(FILE_PATH, Some(65536), dest)
3926
} else {
40-
f.read_exact(dest)?;
41-
}
42-
core::mem::forget(f);
43-
Ok(())
44-
}
45-
46-
fn init_file() -> Result<File, Error> {
47-
if cfg!(target_os = "netbsd") {
48-
// read one byte from "/dev/random" to ensure that OS RNG has initialized
49-
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
27+
use_file(FILE_PATH, None, dest)
5028
}
51-
let f = File::open(FILE_PATH)?;
52-
Ok(f)
5329
}
5430

5531
#[inline(always)]

src/utils.rs

+31-19
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,38 @@
55
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
8-
use crate::Error;
9-
use core::cell::RefCell;
10-
use core::ops::DerefMut;
118

12-
/// If `f` contains `Some(T)` call `use_f` using contents of `f` as an argument,
13-
/// otherwise initialize `f` value using `init_f`, store resulting value in `f`
14-
/// and call `use_f`.
15-
pub(crate) fn use_init<T, F, U>(f: &RefCell<Option<T>>, init_f: F, mut use_f: U)
16-
-> Result<(), Error>
17-
where F: FnOnce() -> Result<T, Error>, U: FnMut(&mut T) -> Result<(), Error>
18-
{
19-
let mut f = f.borrow_mut();
20-
let f: &mut Option<T> = f.deref_mut();
21-
match f {
22-
None => *f = Some(init_f()?),
23-
_ => (),
24-
}
9+
//! Helper function for reading random data from a file.
10+
extern crate std;
11+
12+
use crate::{once::Once, Error};
13+
use std::{
14+
fs::File,
15+
io::Read,
16+
os::unix::io::{FromRawFd, IntoRawFd, RawFd},
17+
};
18+
19+
static RNG_FILE: Once<Result<RawFd, Error>> = Once::new();
20+
21+
pub(crate) fn use_file(path: &str, chunk_len: Option<usize>, dest: &mut [u8]) -> Result<(), Error> {
22+
let result = RNG_FILE.call_once(|| {
23+
if cfg!(target_os = "netbsd") || cfg!(target_os = "linux") || cfg!(target_os = "android") {
24+
// read one byte from "/dev/random" to ensure that OS RNG has initialized
25+
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
26+
}
27+
Ok(File::open(path)?.into_raw_fd())
28+
});
29+
let mut f = match result {
30+
Ok(fd) => unsafe { File::from_raw_fd(*fd) },
31+
Err(e) => return Err(*e),
32+
};
2533

26-
match f {
27-
Some(f) => use_f(f),
28-
None => unreachable!(),
34+
if let Some(chunk_len) = chunk_len {
35+
for chunk in dest.chunks_mut(chunk_len) {
36+
f.read_exact(chunk)?;
37+
}
38+
} else {
39+
f.read_exact(dest)?;
2940
}
41+
Ok(())
3042
}

src/wasi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// except according to those terms.
88

99
//! Implementation for WASI
10+
extern crate std;
11+
1012
use crate::Error;
1113
use core::num::NonZeroU32;
1214
use std::io;

0 commit comments

Comments
 (0)