mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
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:
parent
f3ba6ba32f
commit
922400506b
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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() {
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)?,
|
||||
))
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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"}
|
91
third_party/rust/once_cell/CHANGELOG.md
vendored
91
third_party/rust/once_cell/CHANGELOG.md
vendored
@ -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
197
third_party/rust/once_cell/Cargo.lock
generated
vendored
@ -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"
|
61
third_party/rust/once_cell/Cargo.toml
vendored
61
third_party/rust/once_cell/Cargo.toml
vendored
@ -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 = []
|
201
third_party/rust/once_cell/LICENSE-APACHE
vendored
201
third_party/rust/once_cell/LICENSE-APACHE
vendored
@ -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.
|
23
third_party/rust/once_cell/LICENSE-MIT
vendored
23
third_party/rust/once_cell/LICENSE-MIT
vendored
@ -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.
|
53
third_party/rust/once_cell/README.md
vendored
53
third_party/rust/once_cell/README.md
vendored
@ -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)
|
28
third_party/rust/once_cell/examples/bench.rs
vendored
28
third_party/rust/once_cell/examples/bench.rs
vendored
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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"));
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
fn main() {
|
||||
let cell = once_cell::sync::OnceCell::<u32>::new();
|
||||
cell.get_or_init(|| {
|
||||
cell.get_or_init(|| 1);
|
||||
2
|
||||
});
|
||||
}
|
49
third_party/rust/once_cell/examples/regex.rs
vendored
49
third_party/rust/once_cell/examples/regex.rs
vendored
@ -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());
|
||||
}
|
100
third_party/rust/once_cell/src/imp_pl.rs
vendored
100
third_party/rust/once_cell/src/imp_pl.rs
vendored
@ -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>);
|
||||
}
|
333
third_party/rust/once_cell/src/imp_std.rs
vendored
333
third_party/rust/once_cell/src/imp_std.rs
vendored
@ -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());
|
||||
}
|
||||
}
|
923
third_party/rust/once_cell/src/lib.rs
vendored
923
third_party/rust/once_cell/src/lib.rs
vendored
@ -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() {}
|
||||
}
|
530
third_party/rust/once_cell/tests/test.rs
vendored
530
third_party/rust/once_cell/tests/test.rs
vendored
@ -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()));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user