Skip to main content

relibc/header/pthread/
tls.rs

1// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
2// dropped from src/lib.rs.
3#![warn(warnings, unused_variables)]
4
5use super::*;
6
7// TODO: Hashmap?
8use alloc::{collections::BTreeMap, vec::Vec};
9
10use core::{
11    cell::RefCell,
12    ptr,
13    sync::atomic::{AtomicUsize, Ordering},
14};
15
16use crate::{
17    header::{errno::EINVAL, limits::PTHREAD_DESTRUCTOR_ITERATIONS},
18    sync::Mutex,
19};
20
21type Dtor = Option<extern "C" fn(value: *mut c_void)>;
22
23struct Record {
24    data: *mut c_void,
25}
26
27#[thread_local]
28static VALUES: RefCell<BTreeMap<pthread_key_t, Record>> = RefCell::new(BTreeMap::new());
29static KEYS: Mutex<BTreeMap<pthread_key_t, Dtor>> = Mutex::new(BTreeMap::new());
30static NEXTKEY: AtomicUsize = AtomicUsize::new(1);
31
32/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_getspecific.html>.
33#[unsafe(no_mangle)]
34pub unsafe extern "C" fn pthread_getspecific(key: pthread_key_t) -> *mut c_void {
35    // According to POSIX (issue 8): Calling [`pthread_getspecific`] with a key
36    // that has been deleted with [`pthread_key_delete`] or not obtained from
37    // [`pthread_key_create`] results in undefined behaviour. Therefore, we only
38    // do this check when debug assertions are explicitly enabled to avoid
39    // acquiring the global [`KEYS`] lock when it is not necessary.
40    debug_assert!(KEYS.lock().contains_key(&key));
41    VALUES
42        .borrow_mut()
43        .get(&key)
44        .map(|record| record.data)
45        .unwrap_or(ptr::null_mut())
46}
47
48/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_setspecific.html>.
49#[unsafe(no_mangle)]
50pub unsafe extern "C" fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int {
51    if !KEYS.lock().contains_key(&key) {
52        // We don't have to return anything, but it's not less expensive to ignore it.
53        //println!("Invalid key for pthread_setspecific key {:#0x} value {:p}", key, value);
54        return EINVAL;
55    }
56
57    let mut guard = VALUES.borrow_mut();
58
59    let record = guard.entry(key).or_insert(Record {
60        data: core::ptr::null_mut(),
61    });
62    //println!("Valid key for pthread_setspecific key {:#0x} value {:p} (was {:p})", key, value, record.data);
63
64    record.data = value.cast_mut();
65    0
66}
67
68/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_key_create.html>.
69#[unsafe(no_mangle)]
70pub unsafe extern "C" fn pthread_key_create(
71    key_ptr: *mut pthread_key_t,
72    destructor: Dtor,
73) -> c_int {
74    let key = NEXTKEY.fetch_add(1, Ordering::SeqCst) as pthread_key_t;
75
76    // TODO
77    //if key >= PTHREAD_KEYS_MAX {
78    //}
79
80    KEYS.lock().insert(key, destructor);
81
82    unsafe { key_ptr.write(key) };
83    0
84}
85
86/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_key_delete.html>.
87#[unsafe(no_mangle)]
88pub unsafe extern "C" fn pthread_key_delete(key: pthread_key_t) -> c_int {
89    if KEYS.lock().remove(&key).is_none() {
90        // We don't have to return anything, but it's not less expensive to ignore it.
91        return EINVAL;
92    }
93
94    // POSIX recommends to return EINVAL if the value does not "refers"
95    // to this key, but we do not map VALUES back to KEYS
96    VALUES.borrow_mut().remove(&key);
97
98    0
99}
100
101pub(crate) unsafe fn run_all_destructors() {
102    for _ in 0..PTHREAD_DESTRUCTOR_ITERATIONS {
103        let mut any_run = false;
104        let dtors = {
105            let keys = KEYS.lock();
106            keys.iter()
107                .filter_map(|(&key, &dtor)| dtor.map(|dtor| (key, dtor)))
108                .collect::<Vec<_>>()
109        };
110
111        // According to POSIX (issue 8): There is no specific order in which we
112        // have to run the destructors.
113        for (key, dtor) in dtors {
114            let mut values = VALUES.borrow_mut();
115            if let Some(record) = values.get_mut(&key) {
116                let val = record.data;
117                if val.is_null() {
118                    continue;
119                }
120                record.data = ptr::null_mut();
121                drop(values);
122                dtor(val);
123                any_run = true;
124            }
125        }
126
127        if !any_run {
128            break;
129        }
130    }
131
132    // According to POSIX (issue 8): If even after
133    // [`PTHREAD_DESTRUCTOR_ITERATIONS`] iterations there are still some
134    // non-NULL values with associated destructors, the behaviour is
135    // implementation-defined. We can choose to stop calling them or continue
136    // calling them until none are left. Both musl and glibc choose to stop
137    // calling them so we do the same.
138}