Skip to content

Automatically use the allocation-free API for Rust 1.27.0+. #103

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

Merged
merged 5 commits into from
Aug 2, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ matrix:
- cargo bench
- cargo test --features nightly
- cargo bench --features nightly
- CARGO_CFG_LAZY_STATIC_HEAP_IMPL=1 cargo test
- CARGO_CFG_LAZY_STATIC_HEAP_IMPL=1 cargo bench
- cargo test --features spin_no_std
- cargo bench --features spin_no_std
- cd compiletest
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ repository = "https://github.com/rust-lang-nursery/lazy-static.rs"
keywords = ["macro", "lazy", "static"]
categories = [ "no-std", "rust-patterns", "memory-management" ]

build = "build.rs"

[dependencies.spin]
version = "0.4.6"
optional = true

[build-dependencies]
rustc_version = "0.2.2"

[features]
nightly = []
spin_no_std = ["nightly", "spin"]
Expand Down
52 changes: 52 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
extern crate rustc_version;

use rustc_version::{version, Version};

fn main() {
let is_var_set = |s| std::env::var_os(s).is_some();

// one can manually set a cfg to force an impl -- mostly useful for our own testing
let force_heap_cfg = is_var_set("CARGO_CFG_LAZY_STATIC_HEAP_IMPL");
let force_inline_cfg = is_var_set("CARGO_CFG_LAZY_STATIC_INLINE_IMPL");
let force_spin_cfg = is_var_set("CARGO_CFG_LAZY_STATIC_SPIN_IMPL");

let impls_forced = [force_heap_cfg, force_inline_cfg, force_spin_cfg]
.into_iter()
.filter(|&&f| f)
.count();

assert!(
impls_forced <= 1,
"lazy_static can only be built with one configuration at a time."
);

let nightly_feature_enabled = is_var_set("CARGO_FEATURE_NIGHTLY");
let spin_feature_enabled = is_var_set("CARGO_FEATURE_SPIN_NO_STD");

let version_geq_122 = version().unwrap() >= Version::new(1, 22, 0);
let drop_in_static_supported = version_geq_122 || nightly_feature_enabled;

// precedence:
// 1. explicit requests via cfg or spin_no_std feature
// 2. inline impl with newer rustc version or nightly feature (latter for backcompat)
// 3. fallback to allocating implementation
let impl_name = if force_heap_cfg {
"heap"
} else if force_inline_cfg {
"inline"
} else if force_spin_cfg || spin_feature_enabled {
"spin"
} else if drop_in_static_supported {
"inline"
} else {
"heap"
};

println!("cargo:rustc-cfg=lazy_static_{}_impl", impl_name);

let version_geq_127 = version().unwrap() >= Version::new(1, 27, 0);
let core_unreachable_unchecked_supported = version_geq_127 || nightly_feature_enabled;
if core_unreachable_unchecked_supported {
println!("cargo:rustc-cfg=lazy_static_core_unreachable_unchecked");
}
}
11 changes: 7 additions & 4 deletions src/lazy.rs → src/heap_lazy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ use self::std::prelude::v1::*;
use self::std::sync::Once;
pub use self::std::sync::ONCE_INIT;

pub struct Lazy<T: Sync>(pub *const T, pub Once);
pub struct Lazy<T: Sync>(*const T, Once);

impl<T: Sync> Lazy<T> {
pub const INIT: Self = Lazy(0 as *const T, ONCE_INIT);

#[inline(always)]
pub fn get<F>(&'static mut self, f: F) -> &T
where F: FnOnce() -> T
where
F: FnOnce() -> T,
{
unsafe {
let r = &mut self.0;
Expand All @@ -35,6 +38,6 @@ unsafe impl<T: Sync> Sync for Lazy<T> {}
#[doc(hidden)]
macro_rules! __lazy_static_create {
($NAME:ident, $T:ty) => {
static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy(0 as *const $T, $crate::lazy::ONCE_INIT);
}
static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
};
}
27 changes: 21 additions & 6 deletions src/nightly_lazy.rs → src/inline_lazy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate std;
extern crate core;
extern crate std;

use self::std::prelude::v1::*;
use self::std::sync::Once;
pub use self::std::sync::ONCE_INIT;

pub struct Lazy<T: Sync>(pub Option<T>, pub Once);
pub struct Lazy<T: Sync>(Option<T>, Once);

impl<T: Sync> Lazy<T> {
pub const INIT: Self = Lazy(None, ONCE_INIT);

#[inline(always)]
pub fn get<F>(&'static mut self, f: F) -> &T
where F: FnOnce() -> T
where
F: FnOnce() -> T,
{
{
let r = &mut self.0;
Expand All @@ -28,7 +31,7 @@ impl<T: Sync> Lazy<T> {
unsafe {
match self.0 {
Some(ref x) => x,
None => core::hint::unreachable_unchecked(),
None => unreachable_unchecked(),
}
}
}
Expand All @@ -40,6 +43,18 @@ unsafe impl<T: Sync> Sync for Lazy<T> {}
#[doc(hidden)]
macro_rules! __lazy_static_create {
($NAME:ident, $T:ty) => {
static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy(None, $crate::lazy::ONCE_INIT);
}
static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
};
}

#[cfg(lazy_static_core_unreachable_unchecked)]
use core::hint::unreachable_unchecked;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to bother with core::hint::unreachable_unchecked at all with our polyfill?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, we can wait until the minimum version is raised for some other reason, to switch to the std version.


#[cfg(not(lazy_static_core_unreachable_unchecked))]
/// Polyfill for core::hint::unreachable_unchecked. Included to support Rust prior to 1.27. See
/// [issue #102](https://github.com/rust-lang-nursery/lazy-static.rs/issues/102#issuecomment-400959779)
/// for details.
unsafe fn unreachable_unchecked() -> ! {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to do this yourself, turns out, https://docs.rs/unreachable exists.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think it's reasonable in this case for lazy_static to include this very simple function to avoid bringing in 2 additional dependencies.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that's fine, if the rationalization is added to the comment.

enum Void {}
match std::mem::uninitialized::<Void>() {}
}
13 changes: 7 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,23 @@ no guarantees can be made about them in regard to SemVer stability.

*/

#![cfg_attr(feature="spin_no_std", feature(const_fn))]
#![cfg_attr(feature="nightly", feature(unreachable))]
// NOTE: see build.rs for where these cfg values are set.
#![cfg_attr(lazy_static_spin_impl, feature(const_fn))]

#![doc(html_root_url = "https://docs.rs/lazy_static/1.0.1")]
#![no_std]

#[cfg(not(feature="nightly"))]
#[cfg(lazy_static_heap_impl)]
#[path="heap_lazy.rs"]
#[doc(hidden)]
pub mod lazy;

#[cfg(all(feature="nightly", not(feature="spin_no_std")))]
#[path="nightly_lazy.rs"]
#[cfg(lazy_static_inline_impl)]
#[path="inline_lazy.rs"]
#[doc(hidden)]
pub mod lazy;

#[cfg(all(feature="nightly", feature="spin_no_std"))]
#[cfg(lazy_static_spin_impl)]
#[path="core_lazy.rs"]
#[doc(hidden)]
pub mod lazy;
Expand Down
2 changes: 0 additions & 2 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![cfg_attr(feature="nightly", feature(const_fn))]

#[macro_use]
extern crate lazy_static;
use std::collections::HashMap;
Expand Down