Browse Source

Make thread keys impl thread-safe and handle invalid keys

We still don't actually run the destructors... Which could be fine, but
we should probably run them somewhere.

I have a feeling we might also need to look at this though:
https://github.com/devkitPro/libctru/blob/master/libctru/source/internal.h
pull/5/head
AzureMarker 3 years ago
parent
commit
89cfb145b7
No known key found for this signature in database
GPG Key ID: 47A133F3BF9D03D3
  1. 1
      Cargo.toml
  2. 87
      src/lib.rs

1
Cargo.toml

@ -7,3 +7,4 @@ edition = "2021"
[dependencies] [dependencies]
libc = "0.2.116" libc = "0.2.116"
once_cell = "1.0"

87
src/lib.rs

@ -1,5 +1,7 @@
#![feature(thread_local)] #![feature(thread_local)]
#![feature(const_btree_new)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(clippy::missing_safety_doc)]
/// Call this somewhere to force Rust to link this module. /// Call this somewhere to force Rust to link this module.
/// The call doesn't need to execute, just exist. /// The call doesn't need to execute, just exist.
@ -37,11 +39,7 @@ extern "C" {
fn CondVar_Init(cv: *mut CondVar); fn CondVar_Init(cv: *mut CondVar);
fn CondVar_Wait(cv: *mut CondVar, lock: *mut LightLock); fn CondVar_Wait(cv: *mut CondVar, lock: *mut LightLock);
fn CondVar_WaitTimeout( fn CondVar_WaitTimeout(cv: *mut CondVar, lock: *mut LightLock, timeout_ns: i64) -> libc::c_int;
cv: *mut CondVar,
lock: *mut LightLock,
timeout_ns: i64,
) -> libc::c_int;
fn CondVar_WakeUp(cv: *mut CondVar, num_threads: i32); fn CondVar_WakeUp(cv: *mut CondVar, num_threads: i32);
} }
@ -246,9 +244,7 @@ fn init_rwlock(lock: *mut libc::pthread_rwlock_t) {
let lock = lock as *mut rwlock_clear; let lock = lock as *mut rwlock_clear;
unsafe { unsafe {
if (*lock).initialized { if !(*lock).initialized {
return
} else {
let mut attr = std::mem::MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); let mut attr = std::mem::MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
pthread_mutexattr_init(attr.as_mut_ptr()); pthread_mutexattr_init(attr.as_mut_ptr());
let mut attr = attr.assume_init(); let mut attr = attr.assume_init();
@ -306,7 +302,7 @@ pub unsafe extern "C" fn pthread_rwlock_tryrdlock(
let lock = lock as *mut rwlock_clear; let lock = lock as *mut rwlock_clear;
if pthread_mutex_trylock(&mut (*lock).mutex) != 0 { if pthread_mutex_trylock(&mut (*lock).mutex) != 0 {
return -1 return -1;
} }
while (*lock).writer_active { while (*lock).writer_active {
@ -346,7 +342,7 @@ pub unsafe extern "C" fn pthread_rwlock_trywrlock(
let lock = lock as *mut rwlock_clear; let lock = lock as *mut rwlock_clear;
if pthread_mutex_trylock(&mut (*lock).mutex) != 0 { if pthread_mutex_trylock(&mut (*lock).mutex) != 0 {
return -1 return -1;
} }
while (*lock).writer_active || (*lock).num_readers > 0 { while (*lock).writer_active || (*lock).num_readers > 0 {
@ -403,67 +399,66 @@ pub unsafe extern "C" fn pthread_rwlockattr_destroy(
// THREAD KEYS IMPLEMENTATION FOR RUST STD // THREAD KEYS IMPLEMENTATION FOR RUST STD
use once_cell::sync::Lazy;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::ptr; use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{PoisonError, RwLock};
pub type Key = usize; type Key = usize;
type Destructor = unsafe extern "C" fn(*mut libc::c_void);
type Dtor = unsafe extern "C" fn(*mut u8);
static NEXT_KEY: AtomicUsize = AtomicUsize::new(1); static NEXT_KEY: AtomicUsize = AtomicUsize::new(1);
static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut(); static KEYS: Lazy<RwLock<BTreeMap<Key, Option<Destructor>>>> = Lazy::new(RwLock::default);
#[thread_local] #[thread_local]
static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut(); static mut LOCALS: BTreeMap<Key, *mut libc::c_void> = BTreeMap::new();
unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> {
if KEYS == ptr::null_mut() {
KEYS = Box::into_raw(Box::new(BTreeMap::new()));
}
&mut *KEYS
}
unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> {
if LOCALS == ptr::null_mut() {
LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
}
&mut *LOCALS
}
#[inline] fn is_valid_key(key: Key) -> bool {
pub unsafe fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key { KEYS.read()
let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); .unwrap_or_else(PoisonError::into_inner)
keys().insert(key, dtor); .contains_key(&(key as Key))
key
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pthread_key_create( pub unsafe extern "C" fn pthread_key_create(
key: *mut libc::pthread_key_t, key: *mut libc::pthread_key_t,
dtor: Option<unsafe extern "C" fn(*mut libc::c_void)>, destructor: Option<Destructor>,
) -> libc::c_int { ) -> libc::c_int {
let new_key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); let new_key = NEXT_KEY.fetch_add(1, Ordering::SeqCst);
keys().insert(new_key, std::mem::transmute(dtor)); KEYS.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(new_key, destructor);
*key = new_key as u32; *key = new_key as libc::pthread_key_t;
0 0
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pthread_key_delete(key: libc::pthread_key_t) -> libc::c_int { pub unsafe extern "C" fn pthread_key_delete(key: libc::pthread_key_t) -> libc::c_int {
keys().remove(&(key as usize)); match KEYS
.write()
0 .unwrap_or_else(PoisonError::into_inner)
.remove(&(key as Key))
{
// We had a entry, so it was a valid key.
// It's officially undefined behavior if they use the key after this,
// so don't worry about cleaning up LOCALS, especially since we can't
// clean up every thread's map.
Some(_) => 0,
// The key is unknown
None => libc::EINVAL,
}
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pthread_getspecific(key: libc::pthread_key_t) -> *mut libc::c_void { pub unsafe extern "C" fn pthread_getspecific(key: libc::pthread_key_t) -> *mut libc::c_void {
if let Some(&entry) = locals().get(&(key as usize)) { if let Some(&value) = LOCALS.get(&(key as Key)) {
entry as _ value as _
} else { } else {
// Note: we don't care if the key is invalid, we still return null
ptr::null_mut() ptr::null_mut()
} }
} }
@ -473,6 +468,12 @@ pub unsafe extern "C" fn pthread_setspecific(
key: libc::pthread_key_t, key: libc::pthread_key_t,
value: *const libc::c_void, value: *const libc::c_void,
) -> libc::c_int { ) -> libc::c_int {
locals().insert(key as usize, std::mem::transmute(value)); let key = key as Key;
if !is_valid_key(key) {
return libc::EINVAL;
}
LOCALS.insert(key, value as *mut _);
0 0
} }

Loading…
Cancel
Save