From 271d990bf602b629007a8b8eb659197079358219 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 21 Feb 2019 22:26:29 +0100 Subject: [PATCH 1/3] Rewrite io::lazy --- src/libstd/io/lazy.rs | 101 +++++++++++++++++++++++------------------ src/libstd/io/stdio.rs | 64 ++++++++++++-------------- 2 files changed, 87 insertions(+), 78 deletions(-) diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs index e864aa2c864bb..f47025478039b 100644 --- a/src/libstd/io/lazy.rs +++ b/src/libstd/io/lazy.rs @@ -1,64 +1,77 @@ -use crate::cell::Cell; -use crate::ptr; -use crate::sync::Arc; +use crate::cell::UnsafeCell; +use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::Ordering; use crate::sys_common; use crate::sys_common::mutex::Mutex; +/// Helper for lazy initialization of a static, with a destructor that runs when the main (Rust) +/// thread exits. +/// +/// Currently used only inside the standard library, by the stdio types. +/// +/// # Safety +/// - `UnsafeCell`: We only create a mutable reference during initialization and during the shutdown +/// phase. At both times there can't exist any other references. +/// - Destruction. The `Drop` implementation of `T` should not access references to anything except +/// itself, they are not guaranteed to exist. It should also not rely on other machinery of the +/// standard library to be available. +/// - Initialization. The `init` function for `get` should not call `get` itself, to prevent +/// infinite recursion and acquiring the guard mutex reentrantly. +/// - We use the `Mutex` from `sys::common` because it has a `const` constructor. It currently has +/// UB when acquired reentrantly without calling `init`. pub struct Lazy { - // We never call `lock.init()`, so it is UB to attempt to acquire this mutex reentrantly! - lock: Mutex, - ptr: Cell<*mut Arc>, + guard: Mutex, // Only used to protect initialization. + status: AtomicUsize, + data: UnsafeCell>, } -#[inline] -const fn done() -> *mut Arc { 1_usize as *mut _ } - unsafe impl Sync for Lazy {} +const UNINITIALIZED: usize = 0; +const SHUTDOWN: usize = 1; +const AVAILABLE: usize = 2; + impl Lazy { pub const fn new() -> Lazy { Lazy { - lock: Mutex::new(), - ptr: Cell::new(ptr::null_mut()), + guard: Mutex::new(), + status: AtomicUsize::new(UNINITIALIZED), + data: UnsafeCell::new(None), } } } impl Lazy { - /// Safety: `init` must not call `get` on the variable that is being - /// initialized. - pub unsafe fn get(&'static self, init: fn() -> Arc) -> Option> { - let _guard = self.lock.lock(); - let ptr = self.ptr.get(); - if ptr.is_null() { - Some(self.init(init)) - } else if ptr == done() { - None - } else { - Some((*ptr).clone()) - } - } + pub unsafe fn get(&'static self, init: fn() -> T) -> Option<&T> { + match self.status.load(Ordering::Acquire) { + UNINITIALIZED => { + let _guard = self.guard.lock(); + // Double-check to make sure this `Lazy` didn't get initialized by another + // thread in the small window before we acquired the mutex. + if self.status.load(Ordering::Relaxed) != UNINITIALIZED { + return self.get(init); + } + + // Register an `at_exit` handler that drops `data` when the main thread exits. + let registered = sys_common::at_exit(move || { + *self.data.get() = None; // `T` gets dropped here + self.status.store(SHUTDOWN, Ordering::Release); + }); + if registered.is_err() { + // Registering the handler will only fail if we are already in the shutdown + // phase. In that case don't attempt to initialize. + self.status.store(SHUTDOWN, Ordering::Release); + return None; + } + + // Run the initializer of `T`. + *self.data.get() = Some(init()); + self.status.store(AVAILABLE, Ordering::Release); - // Must only be called with `lock` held - unsafe fn init(&'static self, init: fn() -> Arc) -> Arc { - // If we successfully register an at exit handler, then we cache the - // `Arc` allocation in our own internal box (it will get deallocated by - // the at exit handler). Otherwise we just return the freshly allocated - // `Arc`. - let registered = sys_common::at_exit(move || { - let ptr = { - let _guard = self.lock.lock(); - self.ptr.replace(done()) - }; - drop(Box::from_raw(ptr)) - }); - // This could reentrantly call `init` again, which is a problem - // because our `lock` allows reentrancy! - // That's why `get` is unsafe and requires the caller to ensure no reentrancy happens. - let ret = init(); - if registered.is_ok() { - self.ptr.set(Box::into_raw(Box::new(ret.clone()))); + (*self.data.get()).as_ref() + }, + SHUTDOWN => None, + _ => (*self.data.get()).as_ref(), } - ret } } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 13bf357e2eb8f..e1266cbbf5df8 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -4,7 +4,7 @@ use crate::cell::RefCell; use crate::fmt; use crate::io::lazy::Lazy; use crate::io::{self, Initializer, BufReader, LineWriter}; -use crate::sync::{Arc, Mutex, MutexGuard}; +use crate::sync::{Mutex, MutexGuard}; use crate::sys::stdio; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread::LocalKey; @@ -138,7 +138,7 @@ fn handle_ebadf(r: io::Result, default: T) -> io::Result { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stdin { - inner: Arc>>>, + inner: &'static Mutex>>, } /// A locked reference to the `Stdin` handle. @@ -202,21 +202,19 @@ pub struct StdinLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stdin() -> Stdin { - static INSTANCE: Lazy>>> = Lazy::new(); - return Stdin { - inner: unsafe { - INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown") - }, - }; - - fn stdin_init() -> Arc>>> { - // This must not reentrantly access `INSTANCE` + static STDIN: Lazy>>> = Lazy::new(); + fn stdin_init() -> Mutex>> { let stdin = match stdin_raw() { Ok(stdin) => Maybe::Real(stdin), _ => Maybe::Fake }; + Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin)) + } - Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin))) + Stdin { + inner: unsafe { + STDIN.get(stdin_init).expect("cannot access stdin during shutdown") + }, } } @@ -355,7 +353,7 @@ pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of // stdout (tty or not). Note that if this is not line buffered it // should also flush-on-panic or some form of flush-on-abort. - inner: Arc>>>>, + inner: &'static ReentrantMutex>>>, } /// A locked reference to the `Stdout` handle. @@ -418,20 +416,19 @@ pub struct StdoutLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stdout() -> Stdout { - static INSTANCE: Lazy>>>> = Lazy::new(); - return Stdout { - inner: unsafe { - INSTANCE.get(stdout_init).expect("cannot access stdout during shutdown") - }, - }; - - fn stdout_init() -> Arc>>>> { - // This must not reentrantly access `INSTANCE` + static STDOUT: Lazy>>>> = Lazy::new(); + fn stdout_init() -> ReentrantMutex>>> { let stdout = match stdout_raw() { Ok(stdout) => Maybe::Real(stdout), _ => Maybe::Fake, }; - Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))) + ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))) + } + + Stdout { + inner: unsafe { + STDOUT.get(stdout_init).expect("cannot access stdout during shutdown") + }, } } @@ -513,7 +510,7 @@ impl fmt::Debug for StdoutLock<'_> { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: Arc>>>, + inner: &'static ReentrantMutex>>, } /// A locked reference to the `Stderr` handle. @@ -571,20 +568,19 @@ pub struct StderrLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stderr() -> Stderr { - static INSTANCE: Lazy>>> = Lazy::new(); - return Stderr { - inner: unsafe { - INSTANCE.get(stderr_init).expect("cannot access stderr during shutdown") - }, - }; - - fn stderr_init() -> Arc>>> { - // This must not reentrantly access `INSTANCE` + static STDERR: Lazy>>> = Lazy::new(); + fn stderr_init() -> ReentrantMutex>> { let stderr = match stderr_raw() { Ok(stderr) => Maybe::Real(stderr), _ => Maybe::Fake, }; - Arc::new(ReentrantMutex::new(RefCell::new(stderr))) + ReentrantMutex::new(RefCell::new(stderr)) + } + + Stderr { + inner: unsafe { + STDERR.get(stderr_init).expect("cannot access stderr during shutdown") + }, } } From 1b04b45fc76493c03c0f19cb1264318f0abcd3d0 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 22 Feb 2019 11:50:31 +0100 Subject: [PATCH 2/3] Make construction of raw stdio infallible --- src/libstd/io/stdio.rs | 46 +++++++++----------------------- src/libstd/sys/cloudabi/stdio.rs | 20 +++++--------- src/libstd/sys/redox/stdio.rs | 14 +++++----- src/libstd/sys/sgx/stdio.rs | 12 ++++----- src/libstd/sys/unix/stdio.rs | 14 +++++----- src/libstd/sys/wasm/stdio.rs | 14 +++------- src/libstd/sys/windows/stdio.rs | 15 ++++------- 7 files changed, 48 insertions(+), 87 deletions(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index e1266cbbf5df8..67cc5ba3246b5 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -41,7 +41,7 @@ struct StderrRaw(stdio::Stderr); /// handles is **not** available to raw handles returned from this function. /// /// The returned handle has no external synchronization or buffering. -fn stdin_raw() -> io::Result { stdio::Stdin::new().map(StdinRaw) } +fn stdin_raw() -> StdinRaw { StdinRaw(stdio::Stdin::new()) } /// Constructs a new raw handle to the standard output stream of this process. /// @@ -52,7 +52,7 @@ fn stdin_raw() -> io::Result { stdio::Stdin::new().map(StdinRaw) } /// /// The returned handle has no external synchronization or buffering layered on /// top. -fn stdout_raw() -> io::Result { stdio::Stdout::new().map(StdoutRaw) } +fn stdout_raw() -> StdoutRaw { StdoutRaw(stdio::Stdout::new()) } /// Constructs a new raw handle to the standard error stream of this process. /// @@ -61,7 +61,7 @@ fn stdout_raw() -> io::Result { stdio::Stdout::new().map(StdoutRaw) } /// /// The returned handle has no external synchronization or buffering layered on /// top. -fn stderr_raw() -> io::Result { stdio::Stderr::new().map(StderrRaw) } +fn stderr_raw() -> StderrRaw { StderrRaw(stdio::Stderr::new()) } impl Read for StdinRaw { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } @@ -71,42 +71,32 @@ impl Read for StdinRaw { Initializer::nop() } } + impl Write for StdoutRaw { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } + impl Write for StderrRaw { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } -enum Maybe { - Real(T), - Fake, -} +struct Maybe (T); impl io::Write for Maybe { fn write(&mut self, buf: &[u8]) -> io::Result { - match *self { - Maybe::Real(ref mut w) => handle_ebadf(w.write(buf), buf.len()), - Maybe::Fake => Ok(buf.len()) - } + handle_ebadf(self.0.write(buf), buf.len()) } fn flush(&mut self) -> io::Result<()> { - match *self { - Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()), - Maybe::Fake => Ok(()) - } + handle_ebadf(self.0.flush(), ()) } } impl io::Read for Maybe { fn read(&mut self, buf: &mut [u8]) -> io::Result { - match *self { - Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), 0), - Maybe::Fake => Ok(0) - } + handle_ebadf(self.0.read(buf), 0) } } @@ -204,11 +194,7 @@ pub struct StdinLock<'a> { pub fn stdin() -> Stdin { static STDIN: Lazy>>> = Lazy::new(); fn stdin_init() -> Mutex>> { - let stdin = match stdin_raw() { - Ok(stdin) => Maybe::Real(stdin), - _ => Maybe::Fake - }; - Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin)) + Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, Maybe(stdin_raw()))) } Stdin { @@ -418,11 +404,7 @@ pub struct StdoutLock<'a> { pub fn stdout() -> Stdout { static STDOUT: Lazy>>>> = Lazy::new(); fn stdout_init() -> ReentrantMutex>>> { - let stdout = match stdout_raw() { - Ok(stdout) => Maybe::Real(stdout), - _ => Maybe::Fake, - }; - ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))) + ReentrantMutex::new(RefCell::new(LineWriter::new(Maybe(stdout_raw())))) } Stdout { @@ -570,11 +552,7 @@ pub struct StderrLock<'a> { pub fn stderr() -> Stderr { static STDERR: Lazy>>> = Lazy::new(); fn stderr_init() -> ReentrantMutex>> { - let stderr = match stderr_raw() { - Ok(stderr) => Maybe::Real(stderr), - _ => Maybe::Fake, - }; - ReentrantMutex::new(RefCell::new(stderr)) + ReentrantMutex::new(RefCell::new(Maybe(stderr_raw()))) } Stderr { diff --git a/src/libstd/sys/cloudabi/stdio.rs b/src/libstd/sys/cloudabi/stdio.rs index 601563c5b1fcb..d4ba1caf68ea5 100644 --- a/src/libstd/sys/cloudabi/stdio.rs +++ b/src/libstd/sys/cloudabi/stdio.rs @@ -1,14 +1,12 @@ use crate::io; use crate::sys::cloudabi::abi; -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin(())) - } + pub fn new() -> Stdin { Stdin } } impl io::Read for Stdin { @@ -18,9 +16,7 @@ impl io::Read for Stdin { } impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout(())) - } + pub fn new() -> Stdout { Stdout } } impl io::Write for Stdout { @@ -37,9 +33,7 @@ impl io::Write for Stdout { } impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr(())) - } + pub fn new() -> Stderr { Stderr } } impl io::Write for Stderr { @@ -62,5 +56,5 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { - Stderr::new().ok() + Some(Stderr::new()) } diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/redox/stdio.rs index 33f5bdbb5d358..52d98b0e651cd 100644 --- a/src/libstd/sys/redox/stdio.rs +++ b/src/libstd/sys/redox/stdio.rs @@ -2,12 +2,12 @@ use crate::io; use crate::sys::{cvt, syscall}; use crate::sys::fd::FileDesc; -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; impl Stdin { - pub fn new() -> io::Result { Ok(Stdin(())) } + pub fn new() -> Stdin { Stdin } } impl io::Read for Stdin { @@ -20,7 +20,7 @@ impl io::Read for Stdin { } impl Stdout { - pub fn new() -> io::Result { Ok(Stdout(())) } + pub fn new() -> Stdout { Stdout } } impl io::Write for Stdout { @@ -37,7 +37,7 @@ impl io::Write for Stdout { } impl Stderr { - pub fn new() -> io::Result { Ok(Stderr(())) } + pub fn new() -> Stderr { Stderr } } impl io::Write for Stderr { @@ -60,5 +60,5 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { - Stderr::new().ok() + Some(Stderr::new()) } diff --git a/src/libstd/sys/sgx/stdio.rs b/src/libstd/sys/sgx/stdio.rs index f2c6892bfb7fd..837c13c44fd9e 100644 --- a/src/libstd/sys/sgx/stdio.rs +++ b/src/libstd/sys/sgx/stdio.rs @@ -3,9 +3,9 @@ use fortanix_sgx_abi as abi; use crate::io; use crate::sys::fd::FileDesc; -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; fn with_std_fd R, R>(fd: abi::Fd, f: F) -> R { let fd = FileDesc::new(fd); @@ -15,7 +15,7 @@ fn with_std_fd R, R>(fd: abi::Fd, f: F) -> R { } impl Stdin { - pub fn new() -> io::Result { Ok(Stdin(())) } + pub fn new() -> Stdin { Stdin } } impl io::Read for Stdin { @@ -25,7 +25,7 @@ impl io::Read for Stdin { } impl Stdout { - pub fn new() -> io::Result { Ok(Stdout(())) } + pub fn new() -> Stdout { Stdout } } impl io::Write for Stdout { @@ -39,7 +39,7 @@ impl io::Write for Stdout { } impl Stderr { - pub fn new() -> io::Result { Ok(Stderr(())) } + pub fn new() -> Stderr { Stderr } } impl io::Write for Stderr { diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 35f163bbdb10f..7d8a3234412a1 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -1,12 +1,12 @@ use crate::io; use crate::sys::fd::FileDesc; -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; impl Stdin { - pub fn new() -> io::Result { Ok(Stdin(())) } + pub fn new() -> Stdin { Stdin } } impl io::Read for Stdin { @@ -19,7 +19,7 @@ impl io::Read for Stdin { } impl Stdout { - pub fn new() -> io::Result { Ok(Stdout(())) } + pub fn new() -> Stdout { Stdout } } impl io::Write for Stdout { @@ -36,7 +36,7 @@ impl io::Write for Stdout { } impl Stderr { - pub fn new() -> io::Result { Ok(Stderr(())) } + pub fn new() -> Stderr { Stderr } } impl io::Write for Stderr { @@ -59,5 +59,5 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { - Stderr::new().ok() + Some(Stderr::new()) } diff --git a/src/libstd/sys/wasm/stdio.rs b/src/libstd/sys/wasm/stdio.rs index b8899a9c84746..44b07d7008a2b 100644 --- a/src/libstd/sys/wasm/stdio.rs +++ b/src/libstd/sys/wasm/stdio.rs @@ -6,9 +6,7 @@ pub struct Stdout; pub struct Stderr; impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin) - } + pub fn new() -> Stdin { Stdin } } impl io::Read for Stdin { @@ -18,9 +16,7 @@ impl io::Read for Stdin { } impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) - } + pub fn new() -> Stdout { Stdout } } impl io::Write for Stdout { @@ -35,9 +31,7 @@ impl io::Write for Stdout { } impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) - } + pub fn new() -> Stderr { Stderr } } impl io::Write for Stderr { @@ -59,7 +53,7 @@ pub fn is_ebadf(_err: &io::Error) -> bool { pub fn panic_output() -> Option { if cfg!(feature = "wasm_syscall") { - Stderr::new().ok() + Some(Stderr::new()) } else { None } diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index b1e76b3b755da..797fb1046ffd9 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -126,8 +126,8 @@ fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result { } impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin { surrogate: 0 }) + pub fn new() -> Stdin { + Stdin { surrogate: 0 } } } @@ -160,7 +160,6 @@ impl io::Read for Stdin { } } - // We assume that if the last `u16` is an unpaired surrogate they got sliced apart by our // buffer size, and keep it around for the next read hoping to put them together. // This is a best effort, and may not work if we are not the only reader on Stdin. @@ -243,9 +242,7 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { } impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) - } + pub fn new() -> Stdout { Stdout } } impl io::Write for Stdout { @@ -259,9 +256,7 @@ impl io::Write for Stdout { } impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) - } + pub fn new() -> Stderr { Stderr } } impl io::Write for Stderr { @@ -279,5 +274,5 @@ pub fn is_ebadf(err: &io::Error) -> bool { } pub fn panic_output() -> Option { - Stderr::new().ok() + Some(Stderr::new()) } From b90d70f2d32f5a211146e0553a62d6a1afcb346a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 4 Mar 2019 19:07:55 +0100 Subject: [PATCH 3/3] Hand out Arc's in io::lazy --- src/libstd/io/lazy.rs | 65 ++++++++++++++++++++++-------------------- src/libstd/io/stdio.rs | 8 +++--- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs index f47025478039b..b75e33f8d43ba 100644 --- a/src/libstd/io/lazy.rs +++ b/src/libstd/io/lazy.rs @@ -1,14 +1,21 @@ use crate::cell::UnsafeCell; +use crate::sync::Arc; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering; use crate::sys_common; use crate::sys_common::mutex::Mutex; -/// Helper for lazy initialization of a static, with a destructor that runs when the main (Rust) -/// thread exits. +/// Helper for lazy initialization of a static, with a destructor that attempts to run when the main +/// (Rust) thread exits. /// /// Currently used only inside the standard library, by the stdio types. /// +/// If there are still child threads around when the main thread exits, they get terminated. But +/// there is a small window where they are not yet terminated and may hold a reference to the +/// the data. We therefore store the data in an `Arc`, keep one of the `Arc`'s in the static, and +/// hand out clones. When the `Arc` in the static gets dropped by the `at_exit` handler, the +/// contents will only be dropped if there where no childs threads holding a reference. +/// /// # Safety /// - `UnsafeCell`: We only create a mutable reference during initialization and during the shutdown /// phase. At both times there can't exist any other references. @@ -22,7 +29,7 @@ use crate::sys_common::mutex::Mutex; pub struct Lazy { guard: Mutex, // Only used to protect initialization. status: AtomicUsize, - data: UnsafeCell>, + data: UnsafeCell>>, } unsafe impl Sync for Lazy {} @@ -42,36 +49,32 @@ impl Lazy { } impl Lazy { - pub unsafe fn get(&'static self, init: fn() -> T) -> Option<&T> { - match self.status.load(Ordering::Acquire) { - UNINITIALIZED => { - let _guard = self.guard.lock(); - // Double-check to make sure this `Lazy` didn't get initialized by another - // thread in the small window before we acquired the mutex. - if self.status.load(Ordering::Relaxed) != UNINITIALIZED { - return self.get(init); - } - - // Register an `at_exit` handler that drops `data` when the main thread exits. - let registered = sys_common::at_exit(move || { - *self.data.get() = None; // `T` gets dropped here - self.status.store(SHUTDOWN, Ordering::Release); - }); - if registered.is_err() { - // Registering the handler will only fail if we are already in the shutdown - // phase. In that case don't attempt to initialize. - self.status.store(SHUTDOWN, Ordering::Release); - return None; - } + pub unsafe fn get(&'static self, init: fn() -> T) -> Option> { + if self.status.load(Ordering::Acquire) == UNINITIALIZED { + let _guard = self.guard.lock(); + // Double-check to make sure this `Lazy` didn't get initialized by another + // thread in the small window before we acquired the mutex. + if self.status.load(Ordering::Relaxed) != UNINITIALIZED { + return self.get(init); + } - // Run the initializer of `T`. - *self.data.get() = Some(init()); - self.status.store(AVAILABLE, Ordering::Release); + // Register an `at_exit` handler. + let registered = sys_common::at_exit(move || { + *self.data.get() = None; + // The reference to `Arc` gets dropped above. If there are no other references + // in child threads `T` will be dropped. + self.status.store(SHUTDOWN, Ordering::Release); + }); + if registered.is_err() { + // Registering the handler will only fail if we are already in the shutdown + // phase. In that case don't attempt to initialize. + return None; + } - (*self.data.get()).as_ref() - }, - SHUTDOWN => None, - _ => (*self.data.get()).as_ref(), + // Run the initializer of `T`. + *self.data.get() = Some(Arc::new(init())); + self.status.store(AVAILABLE, Ordering::Release); } + (*self.data.get()).as_ref().cloned() } } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 67cc5ba3246b5..a4c60292ca237 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -4,7 +4,7 @@ use crate::cell::RefCell; use crate::fmt; use crate::io::lazy::Lazy; use crate::io::{self, Initializer, BufReader, LineWriter}; -use crate::sync::{Mutex, MutexGuard}; +use crate::sync::{Arc, Mutex, MutexGuard}; use crate::sys::stdio; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread::LocalKey; @@ -128,7 +128,7 @@ fn handle_ebadf(r: io::Result, default: T) -> io::Result { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stdin { - inner: &'static Mutex>>, + inner: Arc>>>, } /// A locked reference to the `Stdin` handle. @@ -339,7 +339,7 @@ pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of // stdout (tty or not). Note that if this is not line buffered it // should also flush-on-panic or some form of flush-on-abort. - inner: &'static ReentrantMutex>>>, + inner: Arc>>>>, } /// A locked reference to the `Stdout` handle. @@ -492,7 +492,7 @@ impl fmt::Debug for StdoutLock<'_> { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: &'static ReentrantMutex>>, + inner: Arc>>>, } /// A locked reference to the `Stderr` handle.