Skip to content

Commit 0c72017

Browse files
josephlrnewpavlov
authored andcommitted
Remove lazy_static dependancy (#52)
1 parent b1e8154 commit 0c72017

7 files changed

+108
-35
lines changed

.travis.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,14 @@ matrix:
102102

103103
- rust: nightly
104104
env: DESCRIPTION="cross-platform build only"
105+
# Redox: wait for https://github.com/rust-lang/rust/issues/60139
105106
install:
106107
- rustup target add x86_64-sun-solaris
107108
- rustup target add x86_64-unknown-cloudabi
108109
- rustup target add x86_64-unknown-freebsd
109110
- rustup target add x86_64-fuchsia
110111
- rustup target add x86_64-unknown-netbsd
111-
- rustup target add x86_64-unknown-redox
112+
# - rustup target add x86_64-unknown-redox
112113
- rustup target add x86_64-fortanix-unknown-sgx
113114
# For no_std targets
114115
- rustup component add rust-src
@@ -119,7 +120,7 @@ matrix:
119120
- cargo build --target=x86_64-unknown-freebsd --all-features
120121
- cargo build --target=x86_64-fuchsia --all-features
121122
- cargo build --target=x86_64-unknown-netbsd --all-features
122-
- cargo build --target=x86_64-unknown-redox --all-features
123+
# - cargo build --target=x86_64-unknown-redox --all-features
123124
- cargo build --target=x86_64-fortanix-unknown-sgx --all-features
124125
- cargo xbuild --target=x86_64-unknown-uefi
125126
# also test minimum dependency versions are usable
@@ -129,7 +130,7 @@ matrix:
129130
- cargo build --target=x86_64-unknown-freebsd --all-features
130131
- cargo build --target=x86_64-fuchsia --all-features
131132
- cargo build --target=x86_64-unknown-netbsd --all-features
132-
- cargo build --target=x86_64-unknown-redox --all-features
133+
# - cargo build --target=x86_64-unknown-redox --all-features
133134
- cargo build --target=x86_64-fortanix-unknown-sgx --all-features
134135
- cargo xbuild --target=x86_64-unknown-uefi
135136

Cargo.toml

+1-6
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,12 @@ members = ["tests/wasm_bindgen"]
2020
[dependencies]
2121
log = { version = "0.4", optional = true }
2222

23-
[target.'cfg(any(unix, target_os = "wasi"))'.dependencies]
23+
[target.'cfg(any(unix, target_os = "redox", target_os = "wasi"))'.dependencies]
2424
libc = "0.2.54"
2525

26-
# For holding file descriptors
27-
[target.'cfg(any(unix, target_os = "redox"))'.dependencies]
28-
lazy_static = "1.3.0"
29-
3026
[target.wasm32-unknown-unknown.dependencies]
3127
wasm-bindgen = { version = "0.2.29", optional = true }
3228
stdweb = { version = "0.4.9", optional = true }
33-
lazy_static = "1.3.0"
3429

3530
[features]
3631
std = []

src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ extern crate std;
142142

143143
mod error;
144144
pub use crate::error::Error;
145+
145146
#[allow(dead_code)]
146147
mod util;
147-
148-
// These targets need weak linkage to libc randomness functions.
149-
#[cfg(any(target_os = "macos", target_os = "solaris", target_os = "illumos"))]
148+
#[cfg(any(unix, target_os = "redox"))]
149+
#[allow(dead_code)]
150150
mod util_libc;
151151

152152
// System-specific implementations.

src/use_file.rs

+19-11
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@
99
//! Implementations that just need to read from a file
1010
extern crate std;
1111

12+
use crate::util_libc::LazyFd;
1213
use crate::Error;
14+
use core::mem::ManuallyDrop;
1315
use core::num::NonZeroU32;
14-
use lazy_static::lazy_static;
15-
use std::{fs::File, io::Read};
16+
use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
17+
use std::{
18+
fs::File,
19+
io::{self, Read},
20+
};
1621

1722
#[cfg(target_os = "redox")]
1823
const FILE_PATH: &str = "rand:";
@@ -29,28 +34,31 @@ const FILE_PATH: &str = "/dev/urandom";
2934
const FILE_PATH: &str = "/dev/random";
3035

3136
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
32-
lazy_static! {
33-
static ref FILE: Result<File, Error> = init_file();
34-
}
35-
let mut f = FILE.as_ref()?;
37+
static FD: LazyFd = LazyFd::new();
38+
let fd = FD.init(init_file).ok_or(io::Error::last_os_error())?;
39+
let file = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
40+
let mut file_ref: &File = &file;
3641

3742
if cfg!(target_os = "emscripten") {
3843
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
3944
for chunk in dest.chunks_mut(65536) {
40-
f.read_exact(chunk)?;
45+
file_ref.read_exact(chunk)?;
4146
}
4247
} else {
43-
f.read_exact(dest)?;
48+
file_ref.read_exact(dest)?;
4449
}
4550
Ok(())
4651
}
4752

48-
fn init_file() -> Result<File, Error> {
53+
fn init_file() -> Option<RawFd> {
4954
if FILE_PATH == "/dev/urandom" {
5055
// read one byte from "/dev/random" to ensure that OS RNG has initialized
51-
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
56+
File::open("/dev/random")
57+
.ok()?
58+
.read_exact(&mut [0u8; 1])
59+
.ok()?;
5260
}
53-
Ok(File::open(FILE_PATH)?)
61+
Some(File::open(FILE_PATH).ok()?.into_raw_fd())
5462
}
5563

5664
#[inline(always)]

src/util.rs

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

9-
use core::sync::atomic::{AtomicUsize, Ordering};
9+
use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
1010

1111
// This structure represents a laziliy initialized static usize value. Useful
1212
// when it is perferable to just rerun initialization instead of locking.
13+
// Both unsync_init and sync_init will invoke an init() function until it
14+
// succeeds, then return the cached value for future calls.
15+
//
16+
// Both methods support init() "failing". If the init() method returns UNINIT,
17+
// that value will be returned as normal, but will not be cached.
18+
//
19+
// Users should only depend on the _value_ returned by init() functions.
20+
// Specifically, for the following init() function:
21+
// fn init() -> usize {
22+
// a();
23+
// let v = b();
24+
// c();
25+
// v
26+
// }
27+
// the effects of c() or writes to shared memory will not necessarily be
28+
// observed and additional syncronization methods with be needed.
1329
pub struct LazyUsize(AtomicUsize);
1430

1531
impl LazyUsize {
@@ -19,20 +35,44 @@ impl LazyUsize {
1935

2036
// The initialization is not completed.
2137
pub const UNINIT: usize = usize::max_value();
38+
// The initialization is currently running.
39+
pub const ACTIVE: usize = usize::max_value() - 1;
2240

2341
// Runs the init() function at least once, returning the value of some run
24-
// of init(). Unlike std::sync::Once, the init() function may be run
25-
// multiple times. If init() returns UNINIT, future calls to unsync_init()
26-
// will always retry. This makes UNINIT ideal for representing failure.
42+
// of init(). Multiple callers can run their init() functions in parallel.
43+
// init() should always return the same value, if it succeeds.
2744
pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
2845
// Relaxed ordering is fine, as we only have a single atomic variable.
29-
let mut val = self.0.load(Ordering::Relaxed);
46+
let mut val = self.0.load(Relaxed);
3047
if val == Self::UNINIT {
3148
val = init();
32-
self.0.store(val, Ordering::Relaxed);
49+
self.0.store(val, Relaxed);
3350
}
3451
val
3552
}
53+
54+
// Synchronously runs the init() function. Only one caller will have their
55+
// init() function running at a time, and exactly one successful call will
56+
// be run. The init() function should never return LazyUsize::ACTIVE.
57+
pub fn sync_init(&self, init: impl FnOnce() -> usize, mut wait: impl FnMut()) -> usize {
58+
// Common and fast path with no contention. Don't wast time on CAS.
59+
match self.0.load(Relaxed) {
60+
Self::UNINIT | Self::ACTIVE => {}
61+
val => return val,
62+
}
63+
// Relaxed ordering is fine, as we only have a single atomic variable.
64+
loop {
65+
match self.0.compare_and_swap(Self::UNINIT, Self::ACTIVE, Relaxed) {
66+
Self::UNINIT => {
67+
let val = init();
68+
self.0.store(val, Relaxed);
69+
return val;
70+
}
71+
Self::ACTIVE => wait(),
72+
val => return val,
73+
}
74+
}
75+
}
3676
}
3777

3878
// Identical to LazyUsize except with bool instead of usize.

src/util_libc.rs

+26
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,29 @@ impl Weak {
3636
NonNull::new(addr as *mut _)
3737
}
3838
}
39+
40+
pub struct LazyFd(LazyUsize);
41+
42+
impl LazyFd {
43+
pub const fn new() -> Self {
44+
Self(LazyUsize::new())
45+
}
46+
47+
// If init() returns Some(x), x should be nonnegative.
48+
pub fn init(&self, init: impl FnOnce() -> Option<libc::c_int>) -> Option<libc::c_int> {
49+
let fd = self.0.sync_init(
50+
|| match init() {
51+
// OK as val >= 0 and val <= c_int::MAX < usize::MAX
52+
Some(val) => val as usize,
53+
None => LazyUsize::UNINIT,
54+
},
55+
|| unsafe {
56+
libc::usleep(1000);
57+
},
58+
);
59+
match fd {
60+
LazyUsize::UNINIT => None,
61+
val => Some(val as libc::c_int),
62+
}
63+
}
64+
}

src/wasm32_stdweb.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use stdweb::web::error::Error as WebError;
1515
use stdweb::{_js_impl, js};
1616

1717
use crate::Error;
18-
use lazy_static::lazy_static;
18+
use std::sync::Once;
1919

2020
#[derive(Clone, Copy, Debug)]
2121
enum RngSource {
@@ -25,11 +25,14 @@ enum RngSource {
2525

2626
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
2727
assert_eq!(mem::size_of::<usize>(), 4);
28-
29-
lazy_static! {
30-
static ref RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
31-
}
32-
getrandom_fill((*RNG_SOURCE)?, dest)
28+
static ONCE: Once = Once::new();
29+
static mut RNG_SOURCE: Result<RngSource, Error> = Err(Error::UNAVAILABLE);
30+
31+
// SAFETY: RNG_SOURCE is only written once, before being read.
32+
ONCE.call_once(|| unsafe {
33+
RNG_SOURCE = getrandom_init();
34+
});
35+
getrandom_fill(unsafe { RNG_SOURCE }?, dest)
3336
}
3437

3538
fn getrandom_init() -> Result<RngSource, Error> {

0 commit comments

Comments
 (0)