Backed out 2 changesets (bug 1590246) for causing windows bustages on audioipc-client. CLOSED TREE

Backed out changeset 586eef699017 (bug 1590246)
Backed out changeset 37e879bff967 (bug 1590246)
This commit is contained in:
Mihai Alexandru Michis 2019-10-22 03:59:01 +03:00
parent f3ba6ba32f
commit 922400506b
28 changed files with 137 additions and 2781 deletions

8
Cargo.lock generated
View File

@ -135,8 +135,8 @@ dependencies = [
"cubeb-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2088,11 +2088,6 @@ dependencies = [
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "once_cell"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "opaque-debug"
version = "0.2.1"
@ -3977,7 +3972,6 @@ dependencies = [
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
"checksum object 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81afbc5773e99efe9533d8a539dfac37e531dcd0f4eeb41584bae03ccf76d4c2"
"checksum once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed"
"checksum opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51ecbcb821e1bd256d456fe858aaa7f380b63863eab2eb86eee1bd9f33dd6682"
"checksum ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0015e9e8e28ee20c581cfbfe47c650cedeb9ed0721090e0b7ebb10b9cdbcc2"
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"

View File

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The audioipc-2 git repository is: https://github.com/djg/audioipc-2.git
The git commit ID used was 8f2a9e0a8b53d9cb6f80d2a1e92dd0f087b4865d (2019-10-22 12:24:27 +1300)
The git commit ID used was b82970f6106ceec4a8f599eda631775eb5d5afaf (2019-10-11 08:25:07 +1300)

View File

@ -58,13 +58,9 @@ pub struct LengthDelimitedCodec<In, Out> {
enum State {
Length,
Data(usize),
Data(u16),
}
const MAX_MESSAGE_LEN: u64 = 1024 * 1024;
const MESSAGE_LENGTH_SIZE: usize = std::mem::size_of::<u32>();
// TODO: static assert that MAX_MESSAGE_LEN can be encoded into MESSAGE_LENGTH_SIZE.
impl<In, Out> Default for LengthDelimitedCodec<In, Out> {
fn default() -> Self {
LengthDelimitedCodec {
@ -76,27 +72,28 @@ impl<In, Out> Default for LengthDelimitedCodec<In, Out> {
}
impl<In, Out> LengthDelimitedCodec<In, Out> {
// Lengths are encoded as little endian u32
fn decode_length(&mut self, buf: &mut BytesMut) -> io::Result<Option<usize>> {
if buf.len() < MESSAGE_LENGTH_SIZE {
// Lengths are encoded as little endian u16
fn decode_length(&mut self, buf: &mut BytesMut) -> io::Result<Option<u16>> {
if buf.len() < 2 {
// Not enough data
return Ok(None);
}
let n = LittleEndian::read_u32(buf.as_ref());
let n = LittleEndian::read_u16(buf.as_ref());
// Consume the length field
let _ = buf.split_to(MESSAGE_LENGTH_SIZE);
let _ = buf.split_to(2);
Ok(Some(n as usize))
Ok(Some(n))
}
fn decode_data(&mut self, buf: &mut BytesMut, n: usize) -> io::Result<Option<Out>>
fn decode_data(&mut self, buf: &mut BytesMut, n: u16) -> io::Result<Option<Out>>
where
Out: DeserializeOwned + Debug,
{
// At this point, the buffer has already had the required capacity
// reserved. All there is to do is read.
let n = n as usize;
if buf.len() < n {
return Ok(None);
}
@ -131,7 +128,7 @@ where
// Ensure that the buffer has enough space to read the
// incoming payload
buf.reserve(n);
buf.reserve(n as usize);
n
}
@ -147,7 +144,7 @@ where
self.state = State::Length;
// Make sure the buffer has enough space to read the next head
buf.reserve(MESSAGE_LENGTH_SIZE);
buf.reserve(2);
Ok(Some(data))
}
@ -158,17 +155,16 @@ where
fn encode(&mut self, item: Self::In, buf: &mut BytesMut) -> io::Result<()> {
trace!("Attempting to encode");
let encoded_len = serialized_size(&item).unwrap();
if encoded_len > MAX_MESSAGE_LEN {
trace!("oversized message {}", encoded_len);
if encoded_len > 8 * 1024 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"encoded message too big",
));
}
buf.reserve((encoded_len as usize) + MESSAGE_LENGTH_SIZE);
buf.reserve((encoded_len + 2) as usize);
buf.put_u32_le(encoded_len as u32);
buf.put_u16_le(encoded_len as u16);
if let Err(e) = bincode::config()
.limit(encoded_len)

View File

@ -168,19 +168,10 @@ unsafe fn close_platformhandle(handle: PlatformHandleType) {
winapi::um::handleapi::CloseHandle(handle);
}
use std::sync::atomic::{AtomicUsize, Ordering};
static SHM_ID: AtomicUsize = AtomicUsize::new(0);
// Generate a temporary shm_path that is unique to the process. This
// path is used temporarily to create a shm segment, which is then
// immediately deleted from the filesystem while retaining handles to
// the shm to be shared between the server and client.
pub fn get_shm_path() -> PathBuf {
pub fn get_shm_path(dir: &str) -> PathBuf {
let pid = std::process::id();
let shm_id = SHM_ID.fetch_add(1, Ordering::SeqCst);
let mut temp = temp_dir();
temp.push(&format!("cubeb-shm-{}-{}", pid, shm_id));
temp.push(&format!("cubeb-shm-{}-{}", pid, dir));
temp
}
@ -196,15 +187,17 @@ pub use messagestream_win::*;
#[cfg(windows)]
pub fn server_platform_init() {
use winapi::shared::winerror;
use winapi::um::combaseapi;
use winapi::um::objbase;
use winapi::shared::winerror;
unsafe {
let r = combaseapi::CoInitializeEx(std::ptr::null_mut(), objbase::COINIT_MULTITHREADED);
let r = combaseapi::CoInitializeEx(std::ptr::null_mut(),
objbase::COINIT_MULTITHREADED);
assert!(winerror::SUCCEEDED(r));
}
}
#[cfg(unix)]
pub fn server_platform_init() {}
pub fn server_platform_init() {
}

View File

@ -5,12 +5,12 @@
use crate::PlatformHandle;
use crate::PlatformHandleType;
#[cfg(target_os = "linux")]
use audio_thread_priority::RtPriorityThreadInfo;
use cubeb::{self, ffi};
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_uint};
use std::ptr;
#[cfg(target_os = "linux")]
use audio_thread_priority::RtPriorityThreadInfo;
#[derive(Debug, Serialize, Deserialize)]
pub struct Device {

View File

@ -4,12 +4,12 @@
// accompanying file LICENSE for details
use mio_named_pipes;
use std::os::windows::fs::*;
use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use std::sync::atomic::{AtomicUsize, Ordering};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_named_pipes;
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
use std::os::windows::fs::*;
#[derive(Debug)]
pub struct MessageStream(miow::pipe::NamedPipe);
@ -30,7 +30,9 @@ impl MessageStream {
.write(true)
.custom_flags(FILE_FLAG_OVERLAPPED);
let file = opts.open(&pipe_name)?;
unsafe { miow::pipe::NamedPipe::from_raw_handle(file.into_raw_handle()) }
unsafe {
miow::pipe::NamedPipe::from_raw_handle(file.into_raw_handle())
}
};
Ok((MessageStream::new(pipe1), MessageStream::new(pipe2)))
}
@ -43,7 +45,9 @@ impl MessageStream {
self,
handle: &tokio::reactor::Handle,
) -> std::result::Result<AsyncMessageStream, std::io::Error> {
let pipe = unsafe { mio_named_pipes::NamedPipe::from_raw_handle(self.into_raw_handle()) };
let pipe = unsafe {
mio_named_pipes::NamedPipe::from_raw_handle(self.into_raw_handle())
};
Ok(AsyncMessageStream::new(
tokio_named_pipes::NamedPipe::from_pipe(pipe, handle)?,
))

View File

@ -3,13 +3,13 @@
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details
use crate::stream;
use crate::{assert_not_in_callback, run_in_callback};
use crate::{ClientStream, AUDIOIPC_INIT_PARAMS};
#[cfg(target_os = "linux")]
use audio_thread_priority::get_current_thread_info;
use crate::stream;
use crate::{ClientStream, G_SERVER_FD, CPUPOOL_INIT_PARAMS};
#[cfg(not(target_os = "linux"))]
use audio_thread_priority::promote_current_thread_to_real_time;
#[cfg(target_os = "linux")]
use audio_thread_priority::get_current_thread_info;
use audioipc::codec::LengthDelimitedCodec;
use audioipc::frame::{framed, Framed};
use audioipc::platformhandle_passing::{framed_with_platformhandles, FramedWithPlatformHandles};
@ -77,8 +77,23 @@ impl ClientContext {
}
}
// TODO: encapsulate connect, etc inside audioipc.
fn open_server_stream() -> io::Result<audioipc::MessageStream> {
unsafe {
if let Some(fd) = G_SERVER_FD {
return Ok(audioipc::MessageStream::from_raw_fd(fd.as_raw()));
}
Err(io::Error::new(
io::ErrorKind::Other,
"Failed to get server connection.",
))
}
}
#[cfg(target_os = "linux")]
fn promote_thread(rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>) {
fn promote_thread(rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>)
{
match get_current_thread_info() {
Ok(info) => {
let bytes = info.serialize();
@ -92,7 +107,8 @@ fn promote_thread(rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>) {
}
#[cfg(not(target_os = "linux"))]
fn promote_thread(_rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>) {
fn promote_thread(_rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>)
{
match promote_current_thread_to_real_time(0, 48000) {
Ok(_) => {
info!("Audio thread promoted to real-time.");
@ -111,10 +127,8 @@ fn register_thread(callback: Option<extern "C" fn(*const ::std::os::raw::c_char)
}
}
fn promote_and_register_thread(
rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>,
callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
) {
fn promote_and_register_thread(rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>,
callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>) {
promote_thread(rpc);
register_thread(callback);
}
@ -159,10 +173,9 @@ impl rpc::Server for DeviceCollectionServer {
self.cpu_pool.spawn_fn(move || {
run_in_callback(|| {
if devtype.contains(cubeb_backend::DeviceType::INPUT) {
unsafe {
input_cb.unwrap()(ptr::null_mut(), input_user_ptr as *mut c_void)
}
unsafe { input_cb.unwrap()(ptr::null_mut(), input_user_ptr as *mut c_void) }
}
if devtype.contains(cubeb_backend::DeviceType::OUTPUT) {
unsafe {
@ -196,18 +209,15 @@ impl ContextOps for ClientContext {
let (tx_rpc, rx_rpc) = mpsc::channel();
let params = AUDIOIPC_INIT_PARAMS.with(|p| p.replace(None).unwrap());
let server_stream =
unsafe { audioipc::MessageStream::from_raw_fd(params.server_connection) };
let params = CPUPOOL_INIT_PARAMS.with(|p| p.replace(None).unwrap());
let core = core::spawn_thread("AudioIPC Client RPC", move || {
let handle = reactor::Handle::default();
register_thread(params.thread_create_callback);
server_stream
.into_tokio_ipc(&handle)
open_server_stream()
.and_then(|stream| stream.into_tokio_ipc(&handle))
.and_then(|stream| bind_and_send_client(stream, &tx_rpc))
})
.map_err(|_| Error::default())?;
@ -423,6 +433,11 @@ impl Drop for ClientContext {
fn drop(&mut self) {
debug!("ClientContext dropped...");
let _ = send_recv!(self.rpc(), ClientDisconnect => ClientDisconnected);
unsafe {
if G_SERVER_FD.is_some() {
G_SERVER_FD.take().unwrap().close();
}
}
}
}

View File

@ -16,12 +16,14 @@ mod stream;
use crate::context::ClientContext;
use crate::stream::ClientStream;
use audioipc::PlatformHandleType;
use audioipc::{PlatformHandle, PlatformHandleType};
use cubeb_backend::{capi, ffi};
use std::os::raw::{c_char, c_int};
type InitParamsTls = std::cell::RefCell<Option<CpuPoolInitParams>>;
thread_local!(static IN_CALLBACK: std::cell::RefCell<bool> = std::cell::RefCell::new(false));
thread_local!(static AUDIOIPC_INIT_PARAMS: std::cell::RefCell<Option<AudioIpcInitParams>> = std::cell::RefCell::new(None));
thread_local!(static CPUPOOL_INIT_PARAMS: InitParamsTls = std::cell::RefCell::new(None));
// This must match the definition of AudioIpcInitParams in
// dom/media/CubebUtils.cpp in Gecko.
@ -35,6 +37,23 @@ pub struct AudioIpcInitParams {
pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
}
#[derive(Clone, Copy, Debug)]
struct CpuPoolInitParams {
pool_size: usize,
stack_size: usize,
thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
}
impl CpuPoolInitParams {
fn init_with(params: &AudioIpcInitParams) -> Self {
CpuPoolInitParams {
pool_size: params.pool_size,
stack_size: params.stack_size,
thread_create_callback: params.thread_create_callback,
}
}
}
fn set_in_callback(in_callback: bool) {
IN_CALLBACK.with(|b| {
assert_eq!(*b.borrow(), !in_callback);
@ -44,7 +63,7 @@ fn set_in_callback(in_callback: bool) {
fn run_in_callback<F, R>(f: F) -> R
where
F: FnOnce() -> R,
F: FnOnce() -> R
{
set_in_callback(true);
@ -61,6 +80,17 @@ fn assert_not_in_callback() {
});
}
fn set_cpupool_init_params<P>(params: P)
where
P: Into<Option<CpuPoolInitParams>>,
{
CPUPOOL_INIT_PARAMS.with(|p| {
*p.borrow_mut() = params.into();
});
}
static mut G_SERVER_FD: Option<PlatformHandle> = None;
#[no_mangle]
/// Entry point from C code.
pub unsafe extern "C" fn audioipc_client_init(
@ -74,8 +104,16 @@ pub unsafe extern "C" fn audioipc_client_init(
let init_params = &*init_params;
AUDIOIPC_INIT_PARAMS.with(|p| {
*p.borrow_mut() = Some(*init_params);
});
// TODO: Better way to pass extra parameters to Context impl.
if G_SERVER_FD.is_some() {
return cubeb_backend::ffi::CUBEB_ERROR;
}
G_SERVER_FD = PlatformHandle::try_new(init_params.server_connection);
if G_SERVER_FD.is_none() {
return cubeb_backend::ffi::CUBEB_ERROR;
}
let cpupool_init_params = CpuPoolInitParams::init_with(&init_params);
set_cpupool_init_params(cpupool_init_params);
capi::capi_init::<ClientContext>(c, context_name)
}

View File

@ -128,8 +128,10 @@ impl rpc::Server for CallbackServer {
let user_ptr = self.user_ptr;
let cb = self.state_cb.unwrap();
self.cpu_pool.spawn_fn(move || {
run_in_callback(|| unsafe {
cb(ptr::null_mut(), user_ptr as *mut _, state);
run_in_callback(|| {
unsafe {
cb(ptr::null_mut(), user_ptr as *mut _, state);
}
});
Ok(CallbackResp::State)

View File

@ -13,7 +13,7 @@ audio_thread_priority = "0.20.2"
audioipc = { path = "../audioipc" }
cubeb-core = "0.6.0"
futures = "0.1.18"
once_cell = "1.2.0"
lazy_static = "1.2.0"
log = "0.4"
slab = "0.4"
tokio = "0.1"

View File

@ -8,6 +8,8 @@
extern crate error_chain;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
use audio_thread_priority::promote_current_thread_to_real_time;
use audioipc::core;
@ -16,7 +18,6 @@ use audioipc::rpc;
use audioipc::{MessageStream, PlatformHandle, PlatformHandleType};
use futures::sync::oneshot;
use futures::Future;
use once_cell::sync::Lazy;
use std::error::Error;
use std::ffi::{CStr, CString};
use std::os::raw::c_void;
@ -31,12 +32,12 @@ struct CubebContextParams {
backend_name: Option<CString>,
}
static G_CUBEB_CONTEXT_PARAMS: Lazy<Mutex<CubebContextParams>> = Lazy::new(|| {
Mutex::new(CubebContextParams {
lazy_static! {
static ref G_CUBEB_CONTEXT_PARAMS: Mutex<CubebContextParams> = Mutex::new(CubebContextParams {
context_name: CString::new("AudioIPC Server").unwrap(),
backend_name: None,
})
});
});
}
#[allow(deprecated)]
pub mod errors {

View File

@ -3,8 +3,6 @@
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details
#[cfg(target_os = "linux")]
use audio_thread_priority::{promote_thread_to_real_time, RtPriorityThreadInfo};
use audioipc;
use audioipc::codec::LengthDelimitedCodec;
use audioipc::frame::{framed, Framed};
@ -32,6 +30,8 @@ use std::rc::Rc;
use std::{panic, slice};
use tokio::reactor;
use tokio::runtime::current_thread;
#[cfg(target_os = "linux")]
use audio_thread_priority::{RtPriorityThreadInfo, promote_thread_to_real_time};
use crate::errors::*;
@ -152,7 +152,8 @@ struct CubebContextState {
manager: CubebDeviceCollectionManager,
}
thread_local!(static CONTEXT_KEY: RefCell<Option<CubebContextState>> = RefCell::new(None));
type ContextKey = RefCell<Option<CubebContextState>>;
thread_local!(static CONTEXT_KEY:ContextKey = RefCell::new(None));
fn with_local_context<T, F>(f: F) -> T
where
@ -242,11 +243,8 @@ impl ServerStreamCallbacks {
frames
}
_ => {
error!("Unexpected message {:?} during data_callback", r);
// TODO: Return a CUBEB_ERROR result here once
// https://github.com/kinetiknz/cubeb/issues/553 is
// fixed.
0
debug!("Unexpected message {:?} during data_callback", r);
-1
}
}
}
@ -514,14 +512,15 @@ impl CubebServer {
}
}
ServerMessage::ContextRegisterDeviceCollectionChanged(device_type, enable) => self
.process_register_device_collection_changed(
ServerMessage::ContextRegisterDeviceCollectionChanged(device_type, enable) => {
self.process_register_device_collection_changed(
context,
manager,
cubeb::DeviceType::from_bits_truncate(device_type),
enable,
)
.unwrap_or_else(error),
.unwrap_or_else(error)
},
#[cfg(target_os = "linux")]
ServerMessage::PromoteThreadToRealTime(thread_info) => {
@ -535,7 +534,8 @@ impl CubebServer {
}
}
ClientMessage::ThreadPromoted
}
},
};
trace!("process_msg: req={:?}, resp={:?}", msg, resp);
@ -597,11 +597,10 @@ impl CubebServer {
let (stm1, stm2) = MessageStream::anonymous_ipc_pair()?;
debug!("Created callback pair: {:?}-{:?}", stm1, stm2);
let mut shm_path = audioipc::get_shm_path();
shm_path.set_extension("input");
let (input_shm, input_file) = SharedMemWriter::new(&shm_path, audioipc::SHM_AREA_SIZE)?;
shm_path.set_extension("output");
let (output_shm, output_file) = SharedMemReader::new(&shm_path, audioipc::SHM_AREA_SIZE)?;
let (input_shm, input_file) =
SharedMemWriter::new(&audioipc::get_shm_path("input"), audioipc::SHM_AREA_SIZE)?;
let (output_shm, output_file) =
SharedMemReader::new(&audioipc::get_shm_path("output"), audioipc::SHM_AREA_SIZE)?;
// This code is currently running on the Client/Server RPC
// handling thread. We need to move the registration of the
@ -723,8 +722,6 @@ unsafe extern "C" fn data_cb_c(
};
cbs.data_callback(input, output, nframes as isize) as c_long
});
// TODO: Return a CUBEB_ERROR result here once
// https://github.com/kinetiknz/cubeb/issues/553 is fixed.
ok.unwrap_or(0)
}

View File

@ -1 +0,0 @@
{"files":{"CHANGELOG.md":"e8d8f83b01058deafb7db4e6ed23443393bc721098975c3162d4897915480466","Cargo.lock":"07ab6e70f2cffcc635b1b90b271e063087faf220a4f9329860e4422548246caf","Cargo.toml":"cd3cfed92955b145400903fd12ec2b5e02e34b18f1dfc4c4aab160db9f6e3f91","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"be4ea928065420dba72c551d8c565844e28760b33168f8baa00b50505d0713db","examples/bench.rs":"1597a52529f75d6c5ad0b86759a775b1d723dfa810e2016317283b13594219da","examples/bench_vs_lazy_static.rs":"d527294a2e73b53ac5faed8b316dfd1ae2a06adb31384134af21f10ce76333a5","examples/lazy_static.rs":"90541b093ed1d1cbb73f4097ff02cf80657e28264d281d6a31d96a708fdfea90","examples/reentrant_init_deadlocks.rs":"735005c65e2926a4cd210ad385f50927a8b73e009838fee682e0a5700ca84dd2","examples/regex.rs":"4a2e0fb093c7f5bbe0fff8689fc0c670c5334344a1bfda376f5faa98a05d459f","src/imp_pl.rs":"686ab515374e152622e9c47206e26f96bf063f22de9ea6fb4b4306d20427cc7a","src/imp_std.rs":"4c0ceb6b4af03f9328ffe1f014d42d8a2179745b22d4d943e151ef4fccbf84ab","src/lib.rs":"a5abd4f3e33294aa336a9f6503cad255546202ee147253c46852e7b1adcb3874","tests/test.rs":"eacada5b5ca427dcfa803593002c86af5389811db39d410799fab95920383ec7"},"package":"891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed"}

View File

@ -1,91 +0,0 @@
# Changelog
## 1.2.0
- add `sync::OnceCell::get_unchecked`.
## 1.1.0
- implement `Default` for `Lazy`: it creates an empty `Lazy<T>` which is initialized with `T::default` on first access.
- add `OnceCell::get_mut`.
## 1.0.2
- actually add `#![no_std]` attribute if std feature is not enabled.
## 1.0.1
- fix unsoundness in `Lazy<T>` if the initializing function panics. Thanks [@xfix](https://github.com/xfix)!
- implement `RefUnwindSafe` for `Lazy`.
- share more code between `std` and `parking_lot` implementations.
- add F.A.Q section to the docs.
## 1.0.0
- remove `parking_lot` from the list of default features.
- add `std` default feature. Without `std`, only `unsync` module is supported.
- implement `Eq` for `OnceCell`.
- fix wrong `Sync` bound on `sync::Lazy`.
- run the whole test suite with miri.
## 0.2.7
- New implementation of `sync::OnceCell` if `parking_lot` feature is disabled.
It now employs a hand-rolled variant of `std::sync::Once`.
- `sync::OnceCell::get_or_try_init` works without `parking_lot` as well!
- document the effects of `parking_lot` feature: same performance but smaller types.
## 0.2.6
- Updated `Lazy`'s `Deref` impl to requires only `FnOnce` instead of `Fn`
## 0.2.5
- `Lazy` requires only `FnOnce` instead of `Fn`
## 0.2.4
- nicer `fmt::Debug` implementation
## 0.2.3
- update `parking_lot` to `0.9.0`
- fix stacked borrows violation in `unsync::OnceCell::get`
- implement `Clone` for `sync::OnceCell<T> where T: Clone`
## 0.2.2
- add `OnceCell::into_inner` which consumes a cell and returns an option
## 0.2.1
- implement `sync::OnceCell::get_or_try_init` if `parking_lot` feature is enabled
- switch internal `unsafe` implementation of `sync::OnceCell` from `Once` to `Mutex`
- `sync::OnceCell::get_or_init` is twice as fast if cell is already initialized
- implement `std::panic::RefUnwindSafe` and `std::panic::UnwindSafe` for `OnceCell`
- better document behavior around panics
## 0.2.0
- MSRV is now 1.31.1
- `Lazy::new` and `OnceCell::new` are now const-fns
- `unsync_lazy` and `sync_lazy` macros are removed
## 0.1.8
- update crossbeam-utils to 0.6
- enable bors-ng
## 0.1.7
- cells implement `PartialEq` and `From`
- MSRV is down to 1.24.1
- update `parking_lot` to `0.7.1`
## 0.1.6
- `unsync::OnceCell<T>` is `Clone` if `T` is `Clone`.
## 0.1.5
- No changelog until this point :(

197
third_party/rust/once_cell/Cargo.lock generated vendored
View File

@ -1,197 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lock_api"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "once_cell"
version = "1.2.0"
dependencies = [
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scopeguard"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88c3d9193984285d544df4a30c23a4e62ead42edf70a4452ceb76dac1ce05c26"
"checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,61 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "once_cell"
version = "1.2.0"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
exclude = ["*.png", "*.svg", "/Cargo.lock.min", "/.travis.yml", "/run-miri-tests.sh", "rustfmt.toml"]
description = "Single assignment cells and lazy values."
documentation = "https://docs.rs/once_cell"
readme = "README.md"
keywords = ["lazy", "static"]
categories = ["rust-patterns", "memory-management"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/matklad/once_cell"
[[example]]
name = "reentrant_init_deadlocks"
required-features = ["std"]
[[example]]
name = "bench"
required-features = ["std"]
[[example]]
name = "bench_vs_lazy_static"
required-features = ["std"]
[[example]]
name = "lazy_static"
required-features = ["std"]
[[example]]
name = "regex"
required-features = ["std"]
[dependencies.parking_lot]
version = "0.9.0"
optional = true
default_features = false
[dev-dependencies.crossbeam-utils]
version = "0.6.0"
[dev-dependencies.lazy_static]
version = "1.0.0"
[dev-dependencies.regex]
version = "1.2.0"
[features]
default = ["std"]
std = []

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,53 +0,0 @@
<p align="center"><img src="design/logo.png" alt="once_cell" height="300px"></p>
[![Build Status](https://travis-ci.org/matklad/once_cell.svg?branch=master)](https://travis-ci.org/matklad/once_cell)
[![Crates.io](https://img.shields.io/crates/v/once_cell.svg)](https://crates.io/crates/once_cell)
[![API reference](https://docs.rs/once_cell/badge.svg)](https://docs.rs/once_cell/)
# Overview
`once_cell` provides two new cell-like types, `unsync::OnceCell` and `sync::OnceCell`. `OnceCell`
might store arbitrary non-`Copy` types, can be assigned to at most once and provide direct access
to the stored contents. In a nutshell, API looks *roughly* like this:
```rust
impl OnceCell<T> {
fn new() -> OnceCell<T> { ... }
fn set(&self, value: T) -> Result<(), T> { ... }
fn get(&self) -> Option<&T> { ... }
}
```
Note that, like with `RefCell` and `Mutex`, the `set` method requires only a shared reference.
Because of the single assignment restriction `get` can return an `&T` instead of `Ref<T>`
or `MutexGuard<T>`.
`once_cell` also has a `Lazy<T>` type, build on top of `OnceCell` which provides the same API as
the `lazy_static!` macro, but without using any macros:
```rust
use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::Lazy;
static GLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| {
let mut m = HashMap::new();
m.insert(13, "Spica".to_string());
m.insert(74, "Hoyten".to_string());
Mutex::new(m)
});
fn main() {
println!("{:?}", GLOBAL_DATA.lock().unwrap());
}
```
More patterns and use-cases are in the [docs](https://docs.rs/once_cell/)!
# Related crates
* [double-checked-cell](https://github.com/niklasf/double-checked-cell)
* [lazy-init](https://crates.io/crates/lazy-init)
* [lazycell](https://crates.io/crates/lazycell)
* [mitochondria](https://crates.io/crates/mitochondria)
* [lazy_static](https://crates.io/crates/lazy_static)

View File

@ -1,28 +0,0 @@
use std::mem::size_of;
use once_cell::sync::OnceCell;
const N_THREADS: usize = 32;
const N_ROUNDS: usize = 100_000_000;
static CELL: OnceCell<usize> = OnceCell::new();
fn main() {
let start = std::time::Instant::now();
let threads =
(0..N_THREADS).map(|i| std::thread::spawn(move || thread_main(i))).collect::<Vec<_>>();
for thread in threads {
thread.join().unwrap();
}
println!("{:?}", start.elapsed());
println!("size_of::<OnceCell<()>>() = {:?}", size_of::<OnceCell<()>>());
println!("size_of::<OnceCell<bool>>() = {:?}", size_of::<OnceCell<bool>>());
println!("size_of::<OnceCell<u32>>() = {:?}", size_of::<OnceCell<u32>>());
}
fn thread_main(i: usize) {
for _ in 0..N_ROUNDS {
let &value = CELL.get_or_init(|| i);
assert!(value < N_THREADS)
}
}

View File

@ -1,51 +0,0 @@
use lazy_static::lazy_static;
use once_cell::sync::Lazy;
const N_THREADS: usize = 32;
const N_ROUNDS: usize = 100_000_000;
static ONCE_CELL: Lazy<Vec<String>> = Lazy::new(|| vec!["Spica".to_string(), "Hoyten".to_string()]);
lazy_static! {
static ref LAZY_STATIC: Vec<String> = vec!["Spica".to_string(), "Hoyten".to_string()];
}
fn main() {
let once_cell = {
let start = std::time::Instant::now();
let threads = (0..N_THREADS)
.map(|_| std::thread::spawn(move || thread_once_cell()))
.collect::<Vec<_>>();
for thread in threads {
thread.join().unwrap();
}
start.elapsed()
};
let lazy_static = {
let start = std::time::Instant::now();
let threads = (0..N_THREADS)
.map(|_| std::thread::spawn(move || thread_lazy_static()))
.collect::<Vec<_>>();
for thread in threads {
thread.join().unwrap();
}
start.elapsed()
};
println!("once_cell: {:?}", once_cell);
println!("lazy_static: {:?}", lazy_static);
}
fn thread_once_cell() {
for _ in 0..N_ROUNDS {
let len = ONCE_CELL.len();
assert_eq!(len, 2)
}
}
fn thread_lazy_static() {
for _ in 0..N_ROUNDS {
let len = LAZY_STATIC.len();
assert_eq!(len, 2)
}
}

View File

@ -1,36 +0,0 @@
extern crate once_cell;
use once_cell::sync::{Lazy, OnceCell};
use std::collections::HashMap;
static HASHMAP: Lazy<HashMap<u32, &'static str>> = Lazy::new(|| {
let mut m = HashMap::new();
m.insert(0, "foo");
m.insert(1, "bar");
m.insert(2, "baz");
m
});
// Same, but completely without macros
fn hashmap() -> &'static HashMap<u32, &'static str> {
static INSTANCE: OnceCell<HashMap<u32, &'static str>> = OnceCell::new();
INSTANCE.get_or_init(|| {
let mut m = HashMap::new();
m.insert(0, "foo");
m.insert(1, "bar");
m.insert(2, "baz");
m
})
}
fn main() {
// First access to `HASHMAP` initializes it
println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());
// Any further access to `HASHMAP` just returns the computed value
println!("The entry for `1` is \"{}\".", HASHMAP.get(&1).unwrap());
// The same works for function-style:
assert_eq!(hashmap().get(&0), Some(&"foo"));
assert_eq!(hashmap().get(&0), Some(&"bar"));
}

View File

@ -1,7 +0,0 @@
fn main() {
let cell = once_cell::sync::OnceCell::<u32>::new();
cell.get_or_init(|| {
cell.get_or_init(|| 1);
2
});
}

View File

@ -1,49 +0,0 @@
use std::{str::FromStr, time::Instant};
use regex::Regex;
macro_rules! regex {
($re:literal $(,)?) => {{
static RE: once_cell::sync::OnceCell<regex::Regex> = once_cell::sync::OnceCell::new();
RE.get_or_init(|| regex::Regex::new($re).unwrap())
}};
}
fn slow() {
let s = r##"13.28.24.13 - - [10/Mar/2016:19:29:25 +0100] "GET /etc/lib/pChart2/examples/index.php?Action=View&Script=../../../../cnf/db.php HTTP/1.1" 404 151 "-" "HTTP_Request2/2.2.1 (http://pear.php.net/package/http_request2) PHP/5.3.16""##;
let mut total = 0;
for _ in 0..1000 {
let re = Regex::new(
r##"^(\S+) (\S+) (\S+) \[([^]]+)\] "([^"]*)" (\d+) (\d+) "([^"]*)" "([^"]*)"$"##,
)
.unwrap();
let size = usize::from_str(re.captures(s).unwrap().get(7).unwrap().as_str()).unwrap();
total += size;
}
println!("{}", total);
}
fn fast() {
let s = r##"13.28.24.13 - - [10/Mar/2016:19:29:25 +0100] "GET /etc/lib/pChart2/examples/index.php?Action=View&Script=../../../../cnf/db.php HTTP/1.1" 404 151 "-" "HTTP_Request2/2.2.1 (http://pear.php.net/package/http_request2) PHP/5.3.16""##;
let mut total = 0;
for _ in 0..1000 {
let re: &Regex = regex!(
r##"^(\S+) (\S+) (\S+) \[([^]]+)\] "([^"]*)" (\d+) (\d+) "([^"]*)" "([^"]*)"$"##,
);
let size = usize::from_str(re.captures(s).unwrap().get(7).unwrap().as_str()).unwrap();
total += size;
}
println!("{}", total);
}
fn main() {
let t = Instant::now();
slow();
println!("slow: {:?}", t.elapsed());
let t = Instant::now();
fast();
println!("fast: {:?}", t.elapsed());
}

View File

@ -1,100 +0,0 @@
use std::{
cell::UnsafeCell,
panic::{RefUnwindSafe, UnwindSafe},
sync::atomic::{AtomicBool, Ordering},
};
use parking_lot::{lock_api::RawMutex as _RawMutex, RawMutex};
pub(crate) struct OnceCell<T> {
mutex: Mutex,
is_initialized: AtomicBool,
pub(crate) value: UnsafeCell<Option<T>>,
}
// Why do we need `T: Send`?
// Thread A creates a `OnceCell` and shares it with
// scoped thread B, which fills the cell, which is
// then destroyed by A. That is, destructor observes
// a sent value.
unsafe impl<T: Sync + Send> Sync for OnceCell<T> {}
unsafe impl<T: Send> Send for OnceCell<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceCell<T> {}
impl<T: UnwindSafe> UnwindSafe for OnceCell<T> {}
impl<T> OnceCell<T> {
pub(crate) const fn new() -> OnceCell<T> {
OnceCell {
mutex: Mutex::new(),
is_initialized: AtomicBool::new(false),
value: UnsafeCell::new(None),
}
}
/// Safety: synchronizes with store to value via Release/Acquire.
#[inline]
pub(crate) fn is_initialized(&self) -> bool {
self.is_initialized.load(Ordering::Acquire)
}
/// Safety: synchronizes with store to value via `is_initialized` or mutex
/// lock/unlock, writes value only once because of the mutex.
#[cold]
pub(crate) fn initialize<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<T, E>,
{
let _guard = self.mutex.lock();
if !self.is_initialized() {
// We are calling user-supplied function and need to be careful.
// - if it returns Err, we unlock mutex and return without touching anything
// - if it panics, we unlock mutex and propagate panic without touching anything
// - if it calls `set` or `get_or_try_init` re-entrantly, we get a deadlock on
// mutex, which is important for safety. We *could* detect this and panic,
// but that is more complicated
// - finally, if it returns Ok, we store the value and store the flag with
// `Release`, which synchronizes with `Acquire`s.
let value = f()?;
let slot: &mut Option<T> = unsafe { &mut *self.value.get() };
debug_assert!(slot.is_none());
*slot = Some(value);
self.is_initialized.store(true, Ordering::Release);
}
Ok(())
}
}
/// Wrapper around parking_lot's `RawMutex` which has `const fn` new.
struct Mutex {
inner: RawMutex,
}
impl Mutex {
const fn new() -> Mutex {
Mutex { inner: RawMutex::INIT }
}
fn lock(&self) -> MutexGuard<'_> {
self.inner.lock();
MutexGuard { inner: &self.inner }
}
}
struct MutexGuard<'a> {
inner: &'a RawMutex,
}
impl Drop for MutexGuard<'_> {
fn drop(&mut self) {
self.inner.unlock();
}
}
#[test]
#[cfg(pointer_width = "64")]
fn test_size() {
use std::mem::size_of;
assert_eq!(size_of::<OnceCell<u32>>, 2 * size_of::<u32>);
}

View File

@ -1,333 +0,0 @@
// There's a lot of scary concurrent code in this module, but it is copied from
// `std::sync::Once` with two changes:
// * no poisoning
// * init function can fail
use std::{
cell::UnsafeCell,
marker::PhantomData,
panic::{RefUnwindSafe, UnwindSafe},
ptr,
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
thread::{self, Thread},
};
#[derive(Debug)]
pub(crate) struct OnceCell<T> {
// This `state` word is actually an encoded version of just a pointer to a
// `Waiter`, so we add the `PhantomData` appropriately.
state: AtomicUsize,
_marker: PhantomData<*mut Waiter>,
// FIXME: switch to `std::mem::MaybeUninit` once we are ready to bump MSRV
// that far. It was stabilized in 1.36.0, so, if you are reading this and
// it's higher than 1.46.0 outside, please send a PR! ;) (and to the same
// for `Lazy`, while we are at it).
pub(crate) value: UnsafeCell<Option<T>>,
}
// Why do we need `T: Send`?
// Thread A creates a `OnceCell` and shares it with
// scoped thread B, which fills the cell, which is
// then destroyed by A. That is, destructor observes
// a sent value.
unsafe impl<T: Sync + Send> Sync for OnceCell<T> {}
unsafe impl<T: Send> Send for OnceCell<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceCell<T> {}
impl<T: UnwindSafe> UnwindSafe for OnceCell<T> {}
// Three states that a OnceCell can be in, encoded into the lower bits of `state` in
// the OnceCell structure.
const INCOMPLETE: usize = 0x0;
const RUNNING: usize = 0x1;
const COMPLETE: usize = 0x2;
// Mask to learn about the state. All other bits are the queue of waiters if
// this is in the RUNNING state.
const STATE_MASK: usize = 0x3;
// Representation of a node in the linked list of waiters in the RUNNING state.
struct Waiter {
thread: Option<Thread>,
signaled: AtomicBool,
next: *mut Waiter,
}
// Helper struct used to clean up after a closure call with a `Drop`
// implementation to also run on panic.
struct Finish<'a> {
failed: bool,
my_state: &'a AtomicUsize,
}
impl<T> OnceCell<T> {
pub(crate) const fn new() -> OnceCell<T> {
OnceCell {
state: AtomicUsize::new(INCOMPLETE),
_marker: PhantomData,
value: UnsafeCell::new(None),
}
}
/// Safety: synchronizes with store to value via Release/(Acquire|SeqCst).
#[inline]
pub(crate) fn is_initialized(&self) -> bool {
// An `Acquire` load is enough because that makes all the initialization
// operations visible to us, and, this being a fast path, weaker
// ordering helps with performance. This `Acquire` synchronizes with
// `SeqCst` operations on the slow path.
self.state.load(Ordering::Acquire) == COMPLETE
}
/// Safety: synchronizes with store to value via SeqCst read from state,
/// writes value only once because we never get to INCOMPLETE state after a
/// successful write.
#[cold]
pub(crate) fn initialize<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<T, E>,
{
let mut f = Some(f);
let mut res: Result<(), E> = Ok(());
let slot = &self.value;
initialize_inner(&self.state, &mut || {
let f = f.take().unwrap();
match f() {
Ok(value) => {
unsafe { *slot.get() = Some(value) };
true
}
Err(e) => {
res = Err(e);
false
}
}
});
res
}
}
// Note: this is intentionally monomorphic
fn initialize_inner(my_state: &AtomicUsize, init: &mut dyn FnMut() -> bool) -> bool {
// This cold path uses SeqCst consistently because the
// performance difference really does not matter there, and
// SeqCst minimizes the chances of something going wrong.
let mut state = my_state.load(Ordering::SeqCst);
'outer: loop {
match state {
// If we're complete, then there's nothing to do, we just
// jettison out as we shouldn't run the closure.
COMPLETE => return true,
// Otherwise if we see an incomplete state we will attempt to
// move ourselves into the RUNNING state. If we succeed, then
// the queue of waiters starts at null (all 0 bits).
INCOMPLETE => {
let old = my_state.compare_and_swap(state, RUNNING, Ordering::SeqCst);
if old != state {
state = old;
continue;
}
// Run the initialization routine, letting it know if we're
// poisoned or not. The `Finish` struct is then dropped, and
// the `Drop` implementation here is responsible for waking
// up other waiters both in the normal return and panicking
// case.
let mut complete = Finish { failed: true, my_state };
let success = init();
// Difference from std: abort if `init` errored.
complete.failed = !success;
return success;
}
// All other values we find should correspond to the RUNNING
// state with an encoded waiter list in the more significant
// bits. We attempt to enqueue ourselves by moving us to the
// head of the list and bail out if we ever see a state that's
// not RUNNING.
_ => {
assert!(state & STATE_MASK == RUNNING);
let mut node = Waiter {
thread: Some(thread::current()),
signaled: AtomicBool::new(false),
next: ptr::null_mut(),
};
let me = &mut node as *mut Waiter as usize;
assert!(me & STATE_MASK == 0);
while state & STATE_MASK == RUNNING {
node.next = (state & !STATE_MASK) as *mut Waiter;
let old = my_state.compare_and_swap(state, me | RUNNING, Ordering::SeqCst);
if old != state {
state = old;
continue;
}
// Once we've enqueued ourselves, wait in a loop.
// Afterwards reload the state and continue with what we
// were doing from before.
while !node.signaled.load(Ordering::SeqCst) {
thread::park();
}
state = my_state.load(Ordering::SeqCst);
continue 'outer;
}
}
}
}
}
impl Drop for Finish<'_> {
fn drop(&mut self) {
// Swap out our state with however we finished. We should only ever see
// an old state which was RUNNING.
let queue = if self.failed {
// Difference from std: flip back to INCOMPLETE rather than POISONED.
self.my_state.swap(INCOMPLETE, Ordering::SeqCst)
} else {
self.my_state.swap(COMPLETE, Ordering::SeqCst)
};
assert_eq!(queue & STATE_MASK, RUNNING);
// Decode the RUNNING to a list of waiters, then walk that entire list
// and wake them up. Note that it is crucial that after we store `true`
// in the node it can be free'd! As a result we load the `thread` to
// signal ahead of time and then unpark it after the store.
unsafe {
let mut queue = (queue & !STATE_MASK) as *mut Waiter;
while !queue.is_null() {
let next = (*queue).next;
let thread = (*queue).thread.take().unwrap();
(*queue).signaled.store(true, Ordering::SeqCst);
thread.unpark();
queue = next;
}
}
}
}
// These test are snatched from std as well.
#[cfg(test)]
mod tests {
use std::panic;
#[cfg(not(miri))] // miri doesn't support threads
use std::{sync::mpsc::channel, thread};
use super::OnceCell;
impl<T> OnceCell<T> {
fn init(&self, f: impl FnOnce() -> T) {
enum Void {}
let _ = self.initialize(|| Ok::<T, Void>(f()));
}
}
#[test]
fn smoke_once() {
static O: OnceCell<()> = OnceCell::new();
let mut a = 0;
O.init(|| a += 1);
assert_eq!(a, 1);
O.init(|| a += 1);
assert_eq!(a, 1);
}
#[test]
#[cfg(not(miri))] // miri doesn't support threads
fn stampede_once() {
static O: OnceCell<()> = OnceCell::new();
static mut RUN: bool = false;
let (tx, rx) = channel();
for _ in 0..10 {
let tx = tx.clone();
thread::spawn(move || {
for _ in 0..4 {
thread::yield_now()
}
unsafe {
O.init(|| {
assert!(!RUN);
RUN = true;
});
assert!(RUN);
}
tx.send(()).unwrap();
});
}
unsafe {
O.init(|| {
assert!(!RUN);
RUN = true;
});
assert!(RUN);
}
for _ in 0..10 {
rx.recv().unwrap();
}
}
#[test]
#[cfg(not(miri))] // miri doesn't support panics
fn poison_bad() {
static O: OnceCell<()> = OnceCell::new();
// poison the once
let t = panic::catch_unwind(|| {
O.init(|| panic!());
});
assert!(t.is_err());
// we can subvert poisoning, however
let mut called = false;
O.init(|| {
called = true;
});
assert!(called);
// once any success happens, we stop propagating the poison
O.init(|| {});
}
#[test]
#[cfg(not(miri))] // miri doesn't support panics
fn wait_for_force_to_finish() {
static O: OnceCell<()> = OnceCell::new();
// poison the once
let t = panic::catch_unwind(|| {
O.init(|| panic!());
});
assert!(t.is_err());
// make sure someone's waiting inside the once via a force
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
let t1 = thread::spawn(move || {
O.init(|| {
tx1.send(()).unwrap();
rx2.recv().unwrap();
});
});
rx1.recv().unwrap();
// put another waiter on the once
let t2 = thread::spawn(|| {
let mut called = false;
O.init(|| {
called = true;
});
assert!(!called);
});
tx2.send(()).unwrap();
assert!(t1.join().is_ok());
assert!(t2.join().is_ok());
}
}

View File

@ -1,923 +0,0 @@
/*!
# Overview
`once_cell` provides two new cell-like types, `unsync::OnceCell` and `sync::OnceCell`. `OnceCell`
might store arbitrary non-`Copy` types, can be assigned to at most once and provide direct access
to the stored contents. In a nutshell, API looks *roughly* like this:
```rust,ignore
impl<T> OnceCell<T> {
fn new() -> OnceCell<T> { ... }
fn set(&self, value: T) -> Result<(), T> { ... }
fn get(&self) -> Option<&T> { ... }
}
```
Note that, like with `RefCell` and `Mutex`, the `set` method requires only a shared reference.
Because of the single assignment restriction `get` can return an `&T` instead of `Ref<T>`
or `MutexGuard<T>`.
The `sync` flavor is thread-safe (that is, implements [`Sync`]) trait, while the `unsync` one is not.
[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
# Patterns
`OnceCell` might be useful for a variety of patterns.
## Safe Initialization of global data
```rust
use std::{env, io};
use once_cell::sync::OnceCell;
#[derive(Debug)]
pub struct Logger {
// ...
}
static INSTANCE: OnceCell<Logger> = OnceCell::new();
impl Logger {
pub fn global() -> &'static Logger {
INSTANCE.get().expect("logger is not initialized")
}
fn from_cli(args: env::Args) -> Result<Logger, std::io::Error> {
// ...
# Ok(Logger {})
}
}
fn main() {
let logger = Logger::from_cli(env::args()).unwrap();
INSTANCE.set(logger).unwrap();
// use `Logger::global()` from now on
}
```
## Lazy initialized global data
This is essentially `lazy_static!` macro, but without a macro.
```rust
use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::OnceCell;
fn global_data() -> &'static Mutex<HashMap<i32, String>> {
static INSTANCE: OnceCell<Mutex<HashMap<i32, String>>> = OnceCell::new();
INSTANCE.get_or_init(|| {
let mut m = HashMap::new();
m.insert(13, "Spica".to_string());
m.insert(74, "Hoyten".to_string());
Mutex::new(m)
})
}
```
There are also `sync::Lazy` and `unsync::Lazy` convenience types to streamline this pattern:
```rust
use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::Lazy;
static GLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| {
let mut m = HashMap::new();
m.insert(13, "Spica".to_string());
m.insert(74, "Hoyten".to_string());
Mutex::new(m)
});
fn main() {
println!("{:?}", GLOBAL_DATA.lock().unwrap());
}
```
## General purpose lazy evaluation
Unlike `lazy_static!`, `Lazy` works with local variables.
```rust
use once_cell::unsync::Lazy;
fn main() {
let ctx = vec![1, 2, 3];
let thunk = Lazy::new(|| {
ctx.iter().sum::<i32>()
});
assert_eq!(*thunk, 6);
}
```
If you need a lazy field in a struct, you probably should use `OnceCell`
directly, because that will allow you to access `self` during initialization.
```rust
use std::{fs, path::PathBuf};
use once_cell::unsync::OnceCell;
struct Ctx {
config_path: PathBuf,
config: OnceCell<String>,
}
impl Ctx {
pub fn get_config(&self) -> Result<&str, std::io::Error> {
let cfg = self.config.get_or_try_init(|| {
fs::read_to_string(&self.config_path)
})?;
Ok(cfg.as_str())
}
}
```
## Building block
Naturally, it is possible to build other abstractions on top of `OnceCell`.
For example, this is a `regex!` macro which takes a string literal and returns an
*expression* that evaluates to a `&'static Regex`:
```
macro_rules! regex {
($re:literal $(,)?) => {{
static RE: once_cell::sync::OnceCell<regex::Regex> = once_cell::sync::OnceCell::new();
RE.get_or_init(|| regex::Regex::new($re).unwrap())
}};
}
```
This macro can be useful to avoid "compile regex on every loop iteration" problem.
# Comparison with std
|`!Sync` types | Access Mode | Drawbacks |
|----------------------|------------------------|-----------------------------------------------|
|`Cell<T>` | `T` | requires `T: Copy` for `get` |
|`RefCel<T>` | `RefMut<T>` / `Ref<T>` | may panic at runtime |
|`unsync::OnceCell<T>` | `&T` | assignable only once |
|`Sync` types | Access Mode | Drawbacks |
|----------------------|------------------------|-----------------------------------------------|
|`AtomicT` | `T` | works only with certain `Copy` types |
|`Mutex<T>` | `MutexGuard<T>` | may deadlock at runtime, may block the thread |
|`sync::OnceCell<T>` | `&T` | assignable only once, may block the thread |
Technically, calling `get_or_init` will also cause a panic or a deadlock if it recursively calls
itself. However, because the assignment can happen only once, such cases should be more rare than
equivalents with `RefCell` and `Mutex`.
# Minimum Supported `rustc` Version
This crate's minimum supported `rustc` version is `1.31.1`.
If only `std` feature is enabled, MSRV will be updated conservatively.
When using other features, like `parking_lot`, MSRV might be updated more frequently, up to the latest stable.
In both cases, increasing MSRV is *not* considered a semver-breaking change.
# Implementation details
Implementation is based on [`lazy_static`](https://github.com/rust-lang-nursery/lazy-static.rs/)
and [`lazy_cell`](https://github.com/indiv0/lazycell/) crates and `std::sync::Once`. In some sense,
`once_cell` just streamlines and unifies those APIs.
To implement a sync flavor of `OnceCell`, this crates uses either a custom re-implementation of
`std::sync::Once` or `parking_lot::Mutex`. This is controlled by the `parking_lot` feature, which
is enabled by default. Performance is the same for both cases, but `parking_lot` based `OnceCell<T>`
is smaller by up to 16 bytes.
This crate uses unsafe.
# F.A.Q.
**Should I use lazy_static or once_cell?**
To the first approximation, `once_cell` is both more flexible and more convenient than `lazy_static`
and should be preferred.
Unlike `once_cell`, `lazy_static` supports spinlock-based implementation of blocking which works with
`#![no_std]`.
`lazy_static` has received significantly more real world testing, but `once_cell` is also a widely
used crate.
**Should I use sync or unsync flavor?**
Because Rust compiler checks thread safety for you, it's impossible to accidentally use `unsync` where
`sync` is required. So, use `unsync` in single-threaded code and `sync` in multi-threaded. It's easy
to switch between the two if code becomes multi-threaded later.
At the moment, `unsync` has an additional benefit that reentrant initialization causes a panic, which
might be easier to debug than a deadlock.
# Related crates
* [double-checked-cell](https://github.com/niklasf/double-checked-cell)
* [lazy-init](https://crates.io/crates/lazy-init)
* [lazycell](https://crates.io/crates/lazycell)
* [mitochondria](https://crates.io/crates/mitochondria)
* [lazy_static](https://crates.io/crates/lazy_static)
*/
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")]
#[cfg(feature = "parking_lot")]
#[path = "imp_pl.rs"]
mod imp;
#[cfg(feature = "std")]
#[cfg(not(feature = "parking_lot"))]
#[path = "imp_std.rs"]
mod imp;
pub mod unsync {
use core::{
cell::{Cell, UnsafeCell},
fmt,
ops::Deref,
};
#[cfg(feature = "std")]
use std::panic::{RefUnwindSafe, UnwindSafe};
/// A cell which can be written to only once. Not thread safe.
///
/// Unlike `:td::cell::RefCell`, a `OnceCell` provides simple `&`
/// references to the contents.
///
/// # Example
/// ```
/// use once_cell::unsync::OnceCell;
///
/// let cell = OnceCell::new();
/// assert!(cell.get().is_none());
///
/// let value: &String = cell.get_or_init(|| {
/// "Hello, World!".to_string()
/// });
/// assert_eq!(value, "Hello, World!");
/// assert!(cell.get().is_some());
/// ```
pub struct OnceCell<T> {
// Invariant: written to at most once.
inner: UnsafeCell<Option<T>>,
}
// Similarly to a `Sync` bound on `sync::OnceCell`, we can use
// `&unsync::OnceCell` to sneak a `T` through `catch_unwind`,
// by initializing the cell in closure and extracting the value in the
// `Drop`.
#[cfg(feature = "std")]
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceCell<T> {}
#[cfg(feature = "std")]
impl<T: UnwindSafe> UnwindSafe for OnceCell<T> {}
impl<T> Default for OnceCell<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: fmt::Debug> fmt::Debug for OnceCell<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.get() {
Some(v) => f.debug_tuple("OnceCell").field(v).finish(),
None => f.write_str("OnceCell(Uninit)"),
}
}
}
impl<T: Clone> Clone for OnceCell<T> {
fn clone(&self) -> OnceCell<T> {
let res = OnceCell::new();
if let Some(value) = self.get() {
match res.set(value.clone()) {
Ok(()) => (),
Err(_) => unreachable!(),
}
}
res
}
}
impl<T: PartialEq> PartialEq for OnceCell<T> {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl<T: Eq> Eq for OnceCell<T> {}
impl<T> From<T> for OnceCell<T> {
fn from(value: T) -> Self {
OnceCell { inner: UnsafeCell::new(Some(value)) }
}
}
impl<T> OnceCell<T> {
/// Creates a new empty cell.
pub const fn new() -> OnceCell<T> {
OnceCell { inner: UnsafeCell::new(None) }
}
/// Gets the reference to the underlying value.
///
/// Returns `None` if the cell is empty.
pub fn get(&self) -> Option<&T> {
// Safe due to `inner`'s invariant
unsafe { &*self.inner.get() }.as_ref()
}
/// Gets the mutable reference to the underlying value.
///
/// Returns `None` if the cell is empty.
pub fn get_mut(&mut self) -> Option<&mut T> {
// Safe because we have unique access
unsafe { &mut *self.inner.get() }.as_mut()
}
/// Sets the contents of this cell to `value`.
///
/// Returns `Ok(())` if the cell was empty and `Err(value)` if it was
/// full.
///
/// # Example
/// ```
/// use once_cell::unsync::OnceCell;
///
/// let cell = OnceCell::new();
/// assert!(cell.get().is_none());
///
/// assert_eq!(cell.set(92), Ok(()));
/// assert_eq!(cell.set(62), Err(62));
///
/// assert!(cell.get().is_some());
/// ```
pub fn set(&self, value: T) -> Result<(), T> {
let slot = unsafe { &*self.inner.get() };
if slot.is_some() {
return Err(value);
}
let slot = unsafe { &mut *self.inner.get() };
// This is the only place where we set the slot, no races
// due to reentrancy/concurrency are possible, and we've
// checked that slot is currently `None`, so this write
// maintains the `inner`'s invariant.
*slot = Some(value);
Ok(())
}
/// Gets the contents of the cell, initializing it with `f`
/// if the cell was empty.
///
/// # Panics
///
/// If `f` panics, the panic is propagated to the caller, and the cell
/// remains uninitialized.
///
/// It is an error to reentrantly initialize the cell from `f`. Doing
/// so results in a panic.
///
/// # Example
/// ```
/// use once_cell::unsync::OnceCell;
///
/// let cell = OnceCell::new();
/// let value = cell.get_or_init(|| 92);
/// assert_eq!(value, &92);
/// let value = cell.get_or_init(|| unreachable!());
/// assert_eq!(value, &92);
/// ```
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
enum Void {}
match self.get_or_try_init(|| Ok::<T, Void>(f())) {
Ok(val) => val,
Err(void) => match void {},
}
}
/// Gets the contents of the cell, initializing it with `f` if
/// the cell was empty. If the cell was empty and `f` failed, an
/// error is returned.
///
/// # Panics
///
/// If `f` panics, the panic is propagated to the caller, and the cell
/// remains uninitialized.
///
/// It is an error to reentrantly initialize the cell from `f`. Doing
/// so results in a panic.
///
/// # Example
/// ```
/// use once_cell::unsync::OnceCell;
///
/// let cell = OnceCell::new();
/// assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
/// assert!(cell.get().is_none());
/// let value = cell.get_or_try_init(|| -> Result<i32, ()> {
/// Ok(92)
/// });
/// assert_eq!(value, Ok(&92));
/// assert_eq!(cell.get(), Some(&92))
/// ```
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
{
if let Some(val) = self.get() {
return Ok(val);
}
let val = f()?;
assert!(self.set(val).is_ok(), "reentrant init");
Ok(self.get().unwrap())
}
/// Consumes the `OnceCell`, returning the wrapped value.
///
/// Returns `None` if the cell was empty.
///
/// # Examples
///
/// ```
/// use once_cell::unsync::OnceCell;
///
/// let cell: OnceCell<String> = OnceCell::new();
/// assert_eq!(cell.into_inner(), None);
///
/// let cell = OnceCell::new();
/// cell.set("hello".to_string()).unwrap();
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
/// ```
pub fn into_inner(self) -> Option<T> {
// Because `into_inner` takes `self` by value, the compiler statically verifies
// that it is not currently borrowed. So it is safe to move out `Option<T>`.
self.inner.into_inner()
}
}
/// A value which is initialized on the first access.
///
/// # Example
/// ```
/// use once_cell::unsync::Lazy;
///
/// let lazy: Lazy<i32> = Lazy::new(|| {
/// println!("initializing");
/// 92
/// });
/// println!("ready");
/// println!("{}", *lazy);
/// println!("{}", *lazy);
///
/// // Prints:
/// // ready
/// // initializing
/// // 92
/// // 92
/// ```
pub struct Lazy<T, F = fn() -> T> {
cell: OnceCell<T>,
init: Cell<Option<F>>,
}
#[cfg(feature = "std")]
impl<T, F: RefUnwindSafe> RefUnwindSafe for Lazy<T, F> where OnceCell<T>: RefUnwindSafe {}
impl<T: fmt::Debug, F: fmt::Debug> fmt::Debug for Lazy<T, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish()
}
}
impl<T, F> Lazy<T, F> {
/// Creates a new lazy value with the given initializing function.
///
/// # Example
/// ```
/// # fn main() {
/// use once_cell::unsync::Lazy;
///
/// let hello = "Hello, World!".to_string();
///
/// let lazy = Lazy::new(|| hello.to_uppercase());
///
/// assert_eq!(&*lazy, "HELLO, WORLD!");
/// # }
/// ```
pub const fn new(init: F) -> Lazy<T, F> {
Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) }
}
}
impl<T, F: FnOnce() -> T> Lazy<T, F> {
/// Forces the evaluation of this lazy value and returns a reference to
/// the result.
///
/// This is equivalent to the `Deref` impl, but is explicit.
///
/// # Example
/// ```
/// use once_cell::unsync::Lazy;
///
/// let lazy = Lazy::new(|| 92);
///
/// assert_eq!(Lazy::force(&lazy), &92);
/// assert_eq!(&*lazy, &92);
/// ```
pub fn force(this: &Lazy<T, F>) -> &T {
this.cell.get_or_init(|| match this.init.take() {
Some(f) => f(),
None => panic!("Lazy instance has previously been poisoned"),
})
}
}
impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> {
type Target = T;
fn deref(&self) -> &T {
Lazy::force(self)
}
}
impl<T: Default> Default for Lazy<T> {
/// Creates a new lazy value using `Default` as the initializing function.
fn default() -> Lazy<T> {
Lazy::new(T::default)
}
}
}
#[cfg(feature = "std")]
pub mod sync {
use std::{cell::Cell, fmt, hint::unreachable_unchecked, panic::RefUnwindSafe};
use crate::imp::OnceCell as Imp;
/// A thread-safe cell which can be written to only once.
///
/// Unlike `std::sync::Mutex`, a `OnceCell` provides simple `&` references
/// to the contents.
///
/// # Example
/// ```
/// use once_cell::sync::OnceCell;
///
/// static CELL: OnceCell<String> = OnceCell::new();
/// assert!(CELL.get().is_none());
///
/// std::thread::spawn(|| {
/// let value: &String = CELL.get_or_init(|| {
/// "Hello, World!".to_string()
/// });
/// assert_eq!(value, "Hello, World!");
/// }).join().unwrap();
///
/// let value: Option<&String> = CELL.get();
/// assert!(value.is_some());
/// assert_eq!(value.unwrap().as_str(), "Hello, World!");
/// ```
pub struct OnceCell<T>(Imp<T>);
impl<T> Default for OnceCell<T> {
fn default() -> OnceCell<T> {
OnceCell::new()
}
}
impl<T: fmt::Debug> fmt::Debug for OnceCell<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.get() {
Some(v) => f.debug_tuple("OnceCell").field(v).finish(),
None => f.write_str("OnceCell(Uninit)"),
}
}
}
impl<T: Clone> Clone for OnceCell<T> {
fn clone(&self) -> OnceCell<T> {
let res = OnceCell::new();
if let Some(value) = self.get() {
match res.set(value.clone()) {
Ok(()) => (),
Err(_) => unreachable!(),
}
}
res
}
}
impl<T> From<T> for OnceCell<T> {
fn from(value: T) -> Self {
let cell = Self::new();
cell.get_or_init(|| value);
cell
}
}
impl<T: PartialEq> PartialEq for OnceCell<T> {
fn eq(&self, other: &OnceCell<T>) -> bool {
self.get() == other.get()
}
}
impl<T: Eq> Eq for OnceCell<T> {}
impl<T> OnceCell<T> {
/// Creates a new empty cell.
pub const fn new() -> OnceCell<T> {
OnceCell(Imp::new())
}
/// Gets the reference to the underlying value.
///
/// Returns `None` if the cell is empty, or being initialized. This
/// method never blocks.
pub fn get(&self) -> Option<&T> {
if self.0.is_initialized() {
// Safe b/c checked is_initialize
Some(unsafe { self.get_unchecked() })
} else {
None
}
}
/// Gets the mutable reference to the underlying value.
///
/// Returns `None` if the cell is empty.
pub fn get_mut(&mut self) -> Option<&mut T> {
// Safe b/c we have a unique access.
unsafe { &mut *self.0.value.get() }.as_mut()
}
/// Get the reference to the underlying value, without checking if the
/// cell is initialized.
///
/// Safety:
///
/// Caller must ensure that the cell is in initialized state.
pub unsafe fn get_unchecked(&self) -> &T {
debug_assert!(self.0.is_initialized());
let slot: &Option<T> = &*self.0.value.get();
match slot {
Some(value) => value,
// This unsafe does improve performance, see `examples/bench`.
None => {
debug_assert!(false);
unreachable_unchecked()
}
}
}
/// Sets the contents of this cell to `value`.
///
/// Returns `Ok(())` if the cell was empty and `Err(value)` if it was
/// full.
///
/// # Example
/// ```
/// use once_cell::sync::OnceCell;
///
/// static CELL: OnceCell<i32> = OnceCell::new();
///
/// fn main() {
/// assert!(CELL.get().is_none());
///
/// std::thread::spawn(|| {
/// assert_eq!(CELL.set(92), Ok(()));
/// }).join().unwrap();
///
/// assert_eq!(CELL.set(62), Err(62));
/// assert_eq!(CELL.get(), Some(&92));
/// }
/// ```
pub fn set(&self, value: T) -> Result<(), T> {
let mut value = Some(value);
self.get_or_init(|| value.take().unwrap());
match value {
None => Ok(()),
Some(value) => Err(value),
}
}
/// Gets the contents of the cell, initializing it with `f` if the cell
/// was empty.
///
/// Many threads may call `get_or_init` concurrently with different
/// initializing functions, but it is guaranteed that only one function
/// will be executed.
///
/// # Panics
///
/// If `f` panics, the panic is propagated to the caller, and the cell
/// remains uninitialized.
///
/// It is an error to reentrantly initialize the cell from `f`. The
/// exact outcome is unspecified. Current implementation deadlocks, but
/// this may be changed to a panic in the future.
///
/// # Example
/// ```
/// use once_cell::sync::OnceCell;
///
/// let cell = OnceCell::new();
/// let value = cell.get_or_init(|| 92);
/// assert_eq!(value, &92);
/// let value = cell.get_or_init(|| unreachable!());
/// assert_eq!(value, &92);
/// ```
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
enum Void {}
match self.get_or_try_init(|| Ok::<T, Void>(f())) {
Ok(val) => val,
Err(void) => match void {},
}
}
/// Gets the contents of the cell, initializing it with `f` if
/// the cell was empty. If the cell was empty and `f` failed, an
/// error is returned.
///
/// # Panics
///
/// If `f` panics, the panic is propagated to the caller, and
/// the cell remains uninitialized.
///
/// It is an error to reentrantly initialize the cell from `f`.
/// The exact outcome is unspecified. Current implementation
/// deadlocks, but this may be changed to a panic in the future.
///
/// # Example
/// ```
/// use once_cell::sync::OnceCell;
///
/// let cell = OnceCell::new();
/// assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
/// assert!(cell.get().is_none());
/// let value = cell.get_or_try_init(|| -> Result<i32, ()> {
/// Ok(92)
/// });
/// assert_eq!(value, Ok(&92));
/// assert_eq!(cell.get(), Some(&92))
/// ```
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
{
// Fast path check
if let Some(value) = self.get() {
return Ok(value);
}
self.0.initialize(f)?;
// Safe b/c called initialize
debug_assert!(self.0.is_initialized());
Ok(unsafe { self.get_unchecked() })
}
/// Consumes the `OnceCell`, returning the wrapped value. Returns
/// `None` if the cell was empty.
///
/// # Examples
///
/// ```
/// use once_cell::sync::OnceCell;
///
/// let cell: OnceCell<String> = OnceCell::new();
/// assert_eq!(cell.into_inner(), None);
///
/// let cell = OnceCell::new();
/// cell.set("hello".to_string()).unwrap();
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
/// ```
pub fn into_inner(self) -> Option<T> {
// Because `into_inner` takes `self` by value, the compiler statically verifies
// that it is not currently borrowed. So it is safe to move out `Option<T>`.
self.0.value.into_inner()
}
}
/// A value which is initialized on the first access.
///
/// This type is thread-safe and can be used in statics:
///
/// # Example
/// ```
/// use std::collections::HashMap;
///
/// use once_cell::sync::Lazy;
///
/// static HASHMAP: Lazy<HashMap<i32, String>> = Lazy::new(|| {
/// println!("initializing");
/// let mut m = HashMap::new();
/// m.insert(13, "Spica".to_string());
/// m.insert(74, "Hoyten".to_string());
/// m
/// });
///
/// fn main() {
/// println!("ready");
/// std::thread::spawn(|| {
/// println!("{:?}", HASHMAP.get(&13));
/// }).join().unwrap();
/// println!("{:?}", HASHMAP.get(&74));
///
/// // Prints:
/// // ready
/// // initializing
/// // Some("Spica")
/// // Some("Hoyten")
/// }
/// ```
pub struct Lazy<T, F = fn() -> T> {
cell: OnceCell<T>,
init: Cell<Option<F>>,
}
impl<T: fmt::Debug, F: fmt::Debug> fmt::Debug for Lazy<T, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish()
}
}
// We never create a `&F` from a `&Lazy<T, F>` so it is fine
// to not impl `Sync` for `F`
// we do create a `&mut Option<F>` in `force`, but this is
// properly synchronized, so it only happens once
// so it also does not contribute to this impl.
unsafe impl<T, F: Send> Sync for Lazy<T, F> where OnceCell<T>: Sync {}
// auto-derived `Send` impl is OK.
#[cfg(feature = "std")]
impl<T, F: RefUnwindSafe> RefUnwindSafe for Lazy<T, F> where OnceCell<T>: RefUnwindSafe {}
impl<T, F> Lazy<T, F> {
/// Creates a new lazy value with the given initializing
/// function.
pub const fn new(f: F) -> Lazy<T, F> {
Lazy { cell: OnceCell::new(), init: Cell::new(Some(f)) }
}
}
impl<T, F: FnOnce() -> T> Lazy<T, F> {
/// Forces the evaluation of this lazy value and
/// returns a reference to result. This is equivalent
/// to the `Deref` impl, but is explicit.
///
/// # Example
/// ```
/// use once_cell::sync::Lazy;
///
/// let lazy = Lazy::new(|| 92);
///
/// assert_eq!(Lazy::force(&lazy), &92);
/// assert_eq!(&*lazy, &92);
/// ```
pub fn force(this: &Lazy<T, F>) -> &T {
this.cell.get_or_init(|| match this.init.take() {
Some(f) => f(),
None => panic!("Lazy instance has previously been poisoned"),
})
}
}
impl<T, F: FnOnce() -> T> ::std::ops::Deref for Lazy<T, F> {
type Target = T;
fn deref(&self) -> &T {
Lazy::force(self)
}
}
impl<T: Default> Default for Lazy<T> {
/// Creates a new lazy value using `Default` as the initializing function.
fn default() -> Lazy<T> {
Lazy::new(T::default)
}
}
/// ```compile_fail
/// struct S(*mut ());
/// unsafe impl Sync for S {}
///
/// fn share<T: Sync>(_: &T) {}
/// share(&once_cell::sync::OnceCell::<S>::new());
/// ```
///
/// ```compile_fail
/// struct S(*mut ());
/// unsafe impl Sync for S {}
///
/// fn share<T: Sync>(_: &T) {}
/// share(&once_cell::sync::Lazy::<S>::new(|| unimplemented!()));
/// ```
fn _dummy() {}
}

View File

@ -1,530 +0,0 @@
mod unsync {
use core::{
cell::Cell,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
use once_cell::unsync::{Lazy, OnceCell};
#[test]
fn once_cell() {
let c = OnceCell::new();
assert!(c.get().is_none());
c.get_or_init(|| 92);
assert_eq!(c.get(), Some(&92));
c.get_or_init(|| panic!("Kabom!"));
assert_eq!(c.get(), Some(&92));
}
#[test]
fn once_cell_get_mut() {
let mut c = OnceCell::new();
assert!(c.get_mut().is_none());
c.set(90).unwrap();
*c.get_mut().unwrap() += 2;
assert_eq!(c.get_mut(), Some(&mut 92));
}
#[test]
fn once_cell_drop() {
static DROP_CNT: AtomicUsize = AtomicUsize::new(0);
struct Dropper;
impl Drop for Dropper {
fn drop(&mut self) {
DROP_CNT.fetch_add(1, SeqCst);
}
}
let x = OnceCell::new();
x.get_or_init(|| Dropper);
assert_eq!(DROP_CNT.load(SeqCst), 0);
drop(x);
assert_eq!(DROP_CNT.load(SeqCst), 1);
}
#[test]
fn unsync_once_cell_drop_empty() {
let x = OnceCell::<String>::new();
drop(x);
}
#[test]
fn clone() {
let s = OnceCell::new();
let c = s.clone();
assert!(c.get().is_none());
s.set("hello".to_string()).unwrap();
let c = s.clone();
assert_eq!(c.get().map(String::as_str), Some("hello"));
}
#[test]
fn from_impl() {
assert_eq!(OnceCell::from("value").get(), Some(&"value"));
assert_ne!(OnceCell::from("foo").get(), Some(&"bar"));
}
#[test]
fn partialeq_impl() {
assert!(OnceCell::from("value") == OnceCell::from("value"));
assert!(OnceCell::from("foo") != OnceCell::from("bar"));
assert!(OnceCell::<String>::new() == OnceCell::new());
assert!(OnceCell::<String>::new() != OnceCell::from("value".to_owned()));
}
#[test]
fn into_inner() {
let cell: OnceCell<String> = OnceCell::new();
assert_eq!(cell.into_inner(), None);
let cell = OnceCell::new();
cell.set("hello".to_string()).unwrap();
assert_eq!(cell.into_inner(), Some("hello".to_string()));
}
#[test]
fn debug_impl() {
let cell = OnceCell::new();
assert_eq!(format!("{:?}", cell), "OnceCell(Uninit)");
cell.set("hello".to_string()).unwrap();
assert_eq!(format!("{:?}", cell), "OnceCell(\"hello\")");
}
#[test]
fn lazy_new() {
let called = Cell::new(0);
let x = Lazy::new(|| {
called.set(called.get() + 1);
92
});
assert_eq!(called.get(), 0);
let y = *x - 30;
assert_eq!(y, 62);
assert_eq!(called.get(), 1);
let y = *x - 30;
assert_eq!(y, 62);
assert_eq!(called.get(), 1);
}
#[test]
fn lazy_default() {
static CALLED: AtomicUsize = AtomicUsize::new(0);
struct Foo(u8);
impl Default for Foo {
fn default() -> Self {
CALLED.fetch_add(1, SeqCst);
Foo(42)
}
}
let lazy: Lazy<std::sync::Mutex<Foo>> = <_>::default();
assert_eq!(CALLED.load(SeqCst), 0);
assert_eq!(lazy.lock().unwrap().0, 42);
assert_eq!(CALLED.load(SeqCst), 1);
lazy.lock().unwrap().0 = 21;
assert_eq!(lazy.lock().unwrap().0, 21);
assert_eq!(CALLED.load(SeqCst), 1);
}
#[test]
#[cfg(not(miri))] // miri doesn't support panics
#[cfg(feature = "std")]
fn lazy_poisoning() {
let x: Lazy<String> = Lazy::new(|| panic!("kaboom"));
for _ in 0..2 {
let res = std::panic::catch_unwind(|| x.len());
assert!(res.is_err());
}
}
#[test]
fn aliasing_in_get() {
let x = once_cell::unsync::OnceCell::new();
x.set(42).unwrap();
let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option<T>` --+
let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option<T>` |
println!("{}", at_x); // <------- up until here ---------------------------+
}
}
#[cfg(feature = "std")]
mod sync {
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use once_cell::sync::{Lazy, OnceCell};
#[cfg(not(miri))] // miri doesn't support threads
mod scope {
pub(super) use crossbeam_utils::thread::scope;
}
#[cfg(miri)]
mod scope {
pub(super) struct Scope;
#[cfg(miri)]
impl Scope {
pub(super) fn spawn(&self, f: impl FnOnce(())) {
f(());
}
}
#[cfg(miri)]
pub(super) fn scope(f: impl FnOnce(&Scope)) -> Result<(), ()> {
f(&Scope);
Ok(())
}
}
use scope::scope;
#[test]
fn once_cell() {
let c = OnceCell::new();
assert!(c.get().is_none());
scope(|s| {
s.spawn(|_| {
c.get_or_init(|| 92);
assert_eq!(c.get(), Some(&92));
});
})
.unwrap();
c.get_or_init(|| panic!("Kabom!"));
assert_eq!(c.get(), Some(&92));
}
#[test]
fn once_cell_get_mut() {
let mut c = OnceCell::new();
assert!(c.get_mut().is_none());
c.set(90).unwrap();
*c.get_mut().unwrap() += 2;
assert_eq!(c.get_mut(), Some(&mut 92));
}
#[test]
fn once_cell_get_unchecked() {
let c = OnceCell::new();
c.set(92).unwrap();
unsafe {
assert_eq!(c.get_unchecked(), &92);
}
}
#[test]
fn once_cell_drop() {
static DROP_CNT: AtomicUsize = AtomicUsize::new(0);
struct Dropper;
impl Drop for Dropper {
fn drop(&mut self) {
DROP_CNT.fetch_add(1, SeqCst);
}
}
let x = OnceCell::new();
scope(|s| {
s.spawn(|_| {
x.get_or_init(|| Dropper);
assert_eq!(DROP_CNT.load(SeqCst), 0);
drop(x);
});
})
.unwrap();
assert_eq!(DROP_CNT.load(SeqCst), 1);
}
#[test]
fn once_cell_drop_empty() {
let x = OnceCell::<String>::new();
drop(x);
}
#[test]
fn clone() {
let s = OnceCell::new();
let c = s.clone();
assert!(c.get().is_none());
s.set("hello".to_string()).unwrap();
let c = s.clone();
assert_eq!(c.get().map(String::as_str), Some("hello"));
}
#[test]
#[cfg(not(miri))] // miri doesn't support panics
fn get_or_try_init() {
let cell: OnceCell<String> = OnceCell::new();
assert!(cell.get().is_none());
let res =
std::panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() }));
assert!(res.is_err());
assert!(cell.get().is_none());
assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
assert_eq!(
cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())),
Ok(&"hello".to_string())
);
assert_eq!(cell.get(), Some(&"hello".to_string()));
}
#[test]
fn from_impl() {
assert_eq!(OnceCell::from("value").get(), Some(&"value"));
assert_ne!(OnceCell::from("foo").get(), Some(&"bar"));
}
#[test]
fn partialeq_impl() {
assert!(OnceCell::from("value") == OnceCell::from("value"));
assert!(OnceCell::from("foo") != OnceCell::from("bar"));
assert!(OnceCell::<String>::new() == OnceCell::new());
assert!(OnceCell::<String>::new() != OnceCell::from("value".to_owned()));
}
#[test]
fn into_inner() {
let cell: OnceCell<String> = OnceCell::new();
assert_eq!(cell.into_inner(), None);
let cell = OnceCell::new();
cell.set("hello".to_string()).unwrap();
assert_eq!(cell.into_inner(), Some("hello".to_string()));
}
#[test]
fn debug_impl() {
let cell = OnceCell::new();
assert_eq!(format!("{:#?}", cell), "OnceCell(Uninit)");
cell.set(vec!["hello", "world"]).unwrap();
assert_eq!(
format!("{:#?}", cell),
r#"OnceCell(
[
"hello",
"world",
],
)"#
);
}
#[test]
#[cfg(not(miri))] // miri doesn't support processes
fn reentrant_init() {
let examples_dir = {
let mut exe = std::env::current_exe().unwrap();
exe.pop();
exe.pop();
exe.push("examples");
exe
};
let bin = examples_dir
.join("reentrant_init_deadlocks")
.with_extension(std::env::consts::EXE_EXTENSION);
let mut guard = Guard { child: std::process::Command::new(bin).spawn().unwrap() };
std::thread::sleep(std::time::Duration::from_secs(2));
let status = guard.child.try_wait().unwrap();
assert!(status.is_none());
struct Guard {
child: std::process::Child,
}
impl Drop for Guard {
fn drop(&mut self) {
let _ = self.child.kill();
}
}
}
#[test]
fn lazy_new() {
let called = AtomicUsize::new(0);
let x = Lazy::new(|| {
called.fetch_add(1, SeqCst);
92
});
assert_eq!(called.load(SeqCst), 0);
scope(|s| {
s.spawn(|_| {
let y = *x - 30;
assert_eq!(y, 62);
assert_eq!(called.load(SeqCst), 1);
});
})
.unwrap();
let y = *x - 30;
assert_eq!(y, 62);
assert_eq!(called.load(SeqCst), 1);
}
#[test]
fn lazy_default() {
static CALLED: AtomicUsize = AtomicUsize::new(0);
struct Foo(u8);
impl Default for Foo {
fn default() -> Self {
CALLED.fetch_add(1, SeqCst);
Foo(42)
}
}
let lazy: Lazy<std::sync::Mutex<Foo>> = <_>::default();
assert_eq!(CALLED.load(SeqCst), 0);
assert_eq!(lazy.lock().unwrap().0, 42);
assert_eq!(CALLED.load(SeqCst), 1);
lazy.lock().unwrap().0 = 21;
assert_eq!(lazy.lock().unwrap().0, 21);
assert_eq!(CALLED.load(SeqCst), 1);
}
#[test]
#[cfg(not(miri))] // leaks memory
fn static_lazy() {
static XS: Lazy<Vec<i32>> = Lazy::new(|| {
let mut xs = Vec::new();
xs.push(1);
xs.push(2);
xs.push(3);
xs
});
scope(|s| {
s.spawn(|_| {
assert_eq!(&*XS, &vec![1, 2, 3]);
});
})
.unwrap();
assert_eq!(&*XS, &vec![1, 2, 3]);
}
#[test]
#[cfg(not(miri))] // leaks memory
fn static_lazy_via_fn() {
fn xs() -> &'static Vec<i32> {
static XS: OnceCell<Vec<i32>> = OnceCell::new();
XS.get_or_init(|| {
let mut xs = Vec::new();
xs.push(1);
xs.push(2);
xs.push(3);
xs
})
}
assert_eq!(xs(), &vec![1, 2, 3]);
}
#[test]
#[cfg(not(miri))] // miri doesn't support panics
fn lazy_poisoning() {
let x: Lazy<String> = Lazy::new(|| panic!("kaboom"));
for _ in 0..2 {
let res = std::panic::catch_unwind(|| x.len());
assert!(res.is_err());
}
}
#[test]
fn once_cell_is_sync_send() {
fn assert_traits<T: Send + Sync>() {}
assert_traits::<OnceCell<String>>();
assert_traits::<Lazy<String>>();
}
#[test]
#[cfg(not(miri))] // leaks memory
fn eval_once_macro() {
macro_rules! eval_once {
(|| -> $ty:ty {
$($body:tt)*
}) => {{
static ONCE_CELL: OnceCell<$ty> = OnceCell::new();
fn init() -> $ty {
$($body)*
}
ONCE_CELL.get_or_init(init)
}};
}
let fib: &'static Vec<i32> = eval_once! {
|| -> Vec<i32> {
let mut res = vec![1, 1];
for i in 0..10 {
let next = res[i] + res[i + 1];
res.push(next);
}
res
}
};
assert_eq!(fib[5], 8)
}
#[test]
#[cfg(not(miri))] // deadlocks without real threads
fn once_cell_does_not_leak_partially_constructed_boxes() {
let n_tries = 100;
let n_readers = 10;
let n_writers = 3;
const MSG: &str = "Hello, World";
for _ in 0..n_tries {
let cell: OnceCell<String> = OnceCell::new();
scope(|scope| {
for _ in 0..n_readers {
scope.spawn(|_| loop {
if let Some(msg) = cell.get() {
assert_eq!(msg, MSG);
break;
}
});
}
for _ in 0..n_writers {
scope.spawn(|_| cell.set(MSG.to_owned()));
}
})
.unwrap()
}
}
#[test]
#[cfg(not(miri))] // miri doesn't support Barrier
fn get_does_not_block() {
use std::sync::Barrier;
let cell = OnceCell::new();
let barrier = Barrier::new(2);
scope(|scope| {
scope.spawn(|_| {
cell.get_or_init(|| {
barrier.wait();
barrier.wait();
"hello".to_string()
});
});
barrier.wait();
assert_eq!(cell.get(), None);
barrier.wait();
})
.unwrap();
assert_eq!(cell.get(), Some(&"hello".to_string()));
}
}