Implement AtomicRef and use that instead of Lazy strings

This improves thread safety since we're now using actual atomics.
This commit is contained in:
ridiculousfish 2024-01-07 16:05:40 -08:00
parent 1914c3a513
commit 7d7c59611f
2 changed files with 51 additions and 30 deletions

View file

@ -7,6 +7,7 @@ use crate::expand::{
use crate::fallback::fish_wcwidth;
use crate::flog::FLOG;
use crate::future_feature_flags::{feature_test, FeatureFlag};
use crate::global_safety::AtomicRef;
use crate::global_safety::RelaxedAtomicBool;
use crate::libc::MB_CUR_MAX;
use crate::termsize::Termsize;
@ -19,7 +20,7 @@ use bitflags::bitflags;
use core::slice;
use libc::{EINTR, EIO, O_WRONLY, SIGTTOU, SIG_IGN, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
use num_traits::ToPrimitive;
use once_cell::sync::{Lazy, OnceCell};
use once_cell::sync::OnceCell;
use std::env;
use std::ffi::{CStr, CString, OsStr, OsString};
use std::mem;
@ -1023,20 +1024,20 @@ static ELLIPSIS_CHAR: AtomicU32 = AtomicU32::new(0);
/// The character or string to use where text has been truncated (ellipsis if possible, otherwise
/// ...)
pub fn get_ellipsis_str() -> &'static wstr {
unsafe { *ELLIPSIS_STRING }
ELLIPSIS_STRING.load()
}
static mut ELLIPSIS_STRING: Lazy<&'static wstr> = Lazy::new(|| L!(""));
static ELLIPSIS_STRING: AtomicRef<wstr> = AtomicRef::new(&L!(""));
/// Character representing an omitted newline at the end of text.
pub fn get_omitted_newline_str() -> &'static wstr {
unsafe { &OMITTED_NEWLINE_STR }
OMITTED_NEWLINE_STR.load()
}
static mut OMITTED_NEWLINE_STR: Lazy<&'static wstr> = Lazy::new(|| L!(""));
static OMITTED_NEWLINE_STR: AtomicRef<wstr> = AtomicRef::new(&L!(""));
pub fn get_omitted_newline_width() -> usize {
unsafe { OMITTED_NEWLINE_STR.len() }
OMITTED_NEWLINE_STR.load().len()
}
static OBFUSCATION_READ_CHAR: AtomicU32 = AtomicU32::new(0);
@ -1409,8 +1410,16 @@ pub type FilenameRef = Arc<WString>;
/// This function should be called after calling `setlocale()` to perform fish specific locale
/// initialization.
#[widestrs]
pub fn fish_setlocale() {
// Helper to make a static reference to a static &'wstr, from a string literal.
// This is necessary to store them in global atomics, as these can't handle fat pointers.
macro_rules! LL {
($s:literal) => {{
const S: &'static wstr = L!($s);
&S
}};
}
// Use various Unicode symbols if they can be encoded using the current locale, else a simple
// ASCII char alternative. All of the can_be_encoded() invocations should return the same
// true/false value since the code points are in the BMP but we're going to be paranoid. This
@ -1418,37 +1427,25 @@ pub fn fish_setlocale() {
// can_be_encoded() will return false in that case.
if can_be_encoded('\u{2026}') {
ELLIPSIS_CHAR.store(u32::from('\u{2026}'), Ordering::Relaxed);
unsafe {
ELLIPSIS_STRING = Lazy::new(|| "\u{2026}"L);
}
ELLIPSIS_STRING.store(LL!("\u{2026}"));
} else {
ELLIPSIS_CHAR.store(u32::from('$'), Ordering::Relaxed); // "horizontal ellipsis"
unsafe {
ELLIPSIS_STRING = Lazy::new(|| "..."L);
}
ELLIPSIS_STRING.store(LL!("..."));
}
if is_windows_subsystem_for_linux() {
// neither of \u23CE and \u25CF can be displayed in the default fonts on Windows, though
// they can be *encoded* just fine. Use alternative glyphs.
unsafe {
OMITTED_NEWLINE_STR = Lazy::new(|| "\u{00b6}"L); // "pilcrow"
}
OMITTED_NEWLINE_STR.store(LL!("\u{00b6}")); // "pilcrow"
OBFUSCATION_READ_CHAR.store(u32::from('\u{2022}'), Ordering::Relaxed); // "bullet"
} else if is_console_session() {
unsafe {
OMITTED_NEWLINE_STR = Lazy::new(|| "^J"L);
}
OMITTED_NEWLINE_STR.store(LL!("^J"));
OBFUSCATION_READ_CHAR.store(u32::from('*'), Ordering::Relaxed);
} else {
if can_be_encoded('\u{23CE}') {
unsafe {
OMITTED_NEWLINE_STR = Lazy::new(|| "\u{23CE}"L); // "return symbol" (⏎)
}
OMITTED_NEWLINE_STR.store(LL!("\u{23CE}")); // "return symbol" (⏎)
} else {
unsafe {
OMITTED_NEWLINE_STR = Lazy::new(|| "^J"L);
}
OMITTED_NEWLINE_STR.store(LL!("^J"));
}
OBFUSCATION_READ_CHAR.store(
u32::from(if can_be_encoded('\u{25CF}') {
@ -1965,7 +1962,8 @@ pub(crate) use assert_is_locked;
/// session. We err on the side of assuming it's not a console session. This approach isn't
/// bullet-proof and that's OK.
pub fn is_console_session() -> bool {
static IS_CONSOLE_SESSION: Lazy<bool> = Lazy::new(|| {
static IS_CONSOLE_SESSION: OnceCell<bool> = OnceCell::new();
*IS_CONSOLE_SESSION.get_or_init(|| {
const PATH_MAX: usize = libc::PATH_MAX as usize;
let mut tty_name = [0u8; PATH_MAX];
unsafe {
@ -1987,9 +1985,7 @@ pub fn is_console_session() -> bool {
Some(term) => !term.as_bytes().contains(&b'-'),
None => true,
}
});
*IS_CONSOLE_SESSION
})
}
/// Asserts that a slice is alphabetically sorted by a [`&wstr`] `name` field.

View file

@ -1,7 +1,7 @@
use crate::flog::FLOG;
use std::cell::{Ref, RefCell, RefMut};
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use std::sync::MutexGuard;
#[derive(Debug, Default)]
@ -28,6 +28,31 @@ impl Clone for RelaxedAtomicBool {
}
}
/// An atomic reference type, allowing &'static values to be stored.
/// This uses relaxed ordering - it's intended for string literals.
/// Note that because string literals are fat pointers, we can't store one
/// directly in an AtomicPtr, so we store a pointer to the string literal instead!
pub struct AtomicRef<T: ?Sized + 'static>(AtomicPtr<&'static T>);
impl<T: ?Sized> AtomicRef<T> {
pub const fn new(value: &'static &'static T) -> Self {
Self(AtomicPtr::new(
value as *const &'static T as *mut &'static T,
))
}
pub fn load(&self) -> &'static T {
unsafe { *self.0.load(Ordering::Relaxed) }
}
pub fn store(&self, value: &'static &'static T) {
self.0.store(
value as *const &'static T as *mut &'static T,
Ordering::Relaxed,
)
}
}
pub struct SharedFromThisBase<T> {
weak: RefCell<Weak<T>>,
}