From 1b9bf71bbc55bb353b8d54cb7c6182c638aeb80c Mon Sep 17 00:00:00 2001 From: AuYang261 <459461160@qq.com> Date: Wed, 11 Oct 2023 14:05:13 +0800 Subject: [PATCH] add `pthread_key_*` and `pthread_*specific` series syscalls to support TSD --- api/arceos_posix_api/build.rs | 1 + .../src/imp/pthread/condvar.rs | 44 +++++++ api/arceos_posix_api/src/imp/pthread/mod.rs | 1 + api/arceos_posix_api/src/imp/pthread/mutex.rs | 12 ++ api/arceos_posix_api/src/imp/pthread/tsd.rs | 68 ++++++++++ api/arceos_posix_api/src/lib.rs | 13 +- .../c/pthread/basic/expect_info_smp4_fifo.out | 9 +- apps/c/pthread/basic/features.txt | 1 + apps/c/pthread/basic/main.c | 48 ++++++- apps/c/pthread/tsd/expect_info_smp4_fifo.out | 22 ++++ apps/c/pthread/tsd/features.txt | 4 + apps/c/pthread/tsd/main.c | 74 +++++++++++ apps/c/pthread/tsd/test_cmd | 2 + modules/axconfig/defconfig.toml | 3 + modules/axtask/src/api.rs | 2 + modules/axtask/src/lib.rs | 2 + modules/axtask/src/run_queue.rs | 1 + modules/axtask/src/task.rs | 38 ++++++ modules/axtask/src/tsd.rs | 122 ++++++++++++++++++ modules/axtask/src/wait_queue.rs | 9 +- ulib/axlibc/include/limits.h | 2 + ulib/axlibc/include/pthread.h | 32 ++++- ulib/axlibc/src/pthread.rs | 52 ++++++++ ulib/axlibc/src/sys.rs | 2 + 24 files changed, 546 insertions(+), 18 deletions(-) create mode 100644 api/arceos_posix_api/src/imp/pthread/tsd.rs create mode 100644 apps/c/pthread/tsd/expect_info_smp4_fifo.out create mode 100644 apps/c/pthread/tsd/features.txt create mode 100644 apps/c/pthread/tsd/main.c create mode 100644 apps/c/pthread/tsd/test_cmd create mode 100644 modules/axtask/src/tsd.rs diff --git a/api/arceos_posix_api/build.rs b/api/arceos_posix_api/build.rs index 8ca85fa36..2d0f90926 100644 --- a/api/arceos_posix_api/build.rs +++ b/api/arceos_posix_api/build.rs @@ -84,6 +84,7 @@ typedef struct {{ "pthread_attr_t", "pthread_mutex_t", "pthread_mutexattr_t", + "pthread_key_t", "pollfd", "nfds_t", "epoll_event", diff --git a/api/arceos_posix_api/src/imp/pthread/condvar.rs b/api/arceos_posix_api/src/imp/pthread/condvar.rs index 38cf7ea30..cc84a42ef 100644 --- a/api/arceos_posix_api/src/imp/pthread/condvar.rs +++ b/api/arceos_posix_api/src/imp/pthread/condvar.rs @@ -40,6 +40,25 @@ impl Condvar { Ok(()) } + fn timedwait( + &self, + mutex: *mut ctypes::pthread_mutex_t, + abstime: *const ctypes::timespec, + ) -> LinuxResult { + let ret = sys_pthread_mutex_unlock(mutex); + if ret < 0 { + return Err(axerrno::LinuxError::try_from(ret).unwrap()); + } + self.wq + .wait_timeout(core::time::Duration::from(unsafe { *abstime })); + + let ret = sys_pthread_mutex_lock(mutex); + if ret < 0 { + return Err(axerrno::LinuxError::try_from(ret).unwrap()); + } + Ok(()) + } + fn notify_one(&self) -> LinuxResult { self.wq.notify_one(true); Ok(()) @@ -63,6 +82,31 @@ pub unsafe fn sys_pthread_cond_init( }) } +/// Destroy a condition variable +pub unsafe fn sys_pthread_cond_destroy(condvar: *mut ctypes::pthread_cond_t) -> c_int { + debug!("sys_pthread_cond_destroy <= {:#x}", condvar as usize); + syscall_body!(sys_pthread_cond_destroy, { + condvar.cast::().drop_in_place(); + Ok(0) + }) +} + +/// Wait for the condition variable to be signaled or timeout +pub unsafe fn sys_pthread_cond_timedwait( + condvar: *mut ctypes::pthread_cond_t, + mutex: *mut ctypes::pthread_mutex_t, + abstime: *const ctypes::timespec, +) -> c_int { + debug!( + "sys_pthread_cond_timedwait <= {:#x}, {:#x}, {:#x}", + condvar as usize, mutex as usize, abstime as usize + ); + syscall_body!(sys_pthread_cond_timedwait, { + (*condvar.cast::()).timedwait(mutex, abstime)?; + Ok(0) + }) +} + /// Wait for the condition variable to be signaled pub unsafe fn sys_pthread_cond_wait( condvar: *mut ctypes::pthread_cond_t, diff --git a/api/arceos_posix_api/src/imp/pthread/mod.rs b/api/arceos_posix_api/src/imp/pthread/mod.rs index fa79e4b86..01d5479cd 100644 --- a/api/arceos_posix_api/src/imp/pthread/mod.rs +++ b/api/arceos_posix_api/src/imp/pthread/mod.rs @@ -19,6 +19,7 @@ use crate::ctypes; pub mod condvar; pub mod mutex; +pub mod tsd; lazy_static::lazy_static! { static ref TID_TO_PTHREAD: RwLock>> = { diff --git a/api/arceos_posix_api/src/imp/pthread/mutex.rs b/api/arceos_posix_api/src/imp/pthread/mutex.rs index 94a255b29..8e872676a 100644 --- a/api/arceos_posix_api/src/imp/pthread/mutex.rs +++ b/api/arceos_posix_api/src/imp/pthread/mutex.rs @@ -64,6 +64,18 @@ pub fn sys_pthread_mutex_init( }) } +/// Destroy the given mutex. +pub fn sys_pthread_mutex_destroy(mutex: *mut ctypes::pthread_mutex_t) -> c_int { + debug!("sys_pthread_mutex_destroy <= {:#x}", mutex as usize); + syscall_body!(sys_pthread_mutex_destroy, { + check_null_mut_ptr(mutex)?; + unsafe { + mutex.cast::().drop_in_place(); + } + Ok(0) + }) +} + /// Lock the given mutex. pub fn sys_pthread_mutex_lock(mutex: *mut ctypes::pthread_mutex_t) -> c_int { debug!("sys_pthread_mutex_lock <= {:#x}", mutex as usize); diff --git a/api/arceos_posix_api/src/imp/pthread/tsd.rs b/api/arceos_posix_api/src/imp/pthread/tsd.rs new file mode 100644 index 000000000..3d0a724c1 --- /dev/null +++ b/api/arceos_posix_api/src/imp/pthread/tsd.rs @@ -0,0 +1,68 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Rukos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use crate::ctypes; +use axerrno::LinuxError; +use axtask::tsd::DestrFunction; +use core::ffi::{c_int, c_void}; + +/// Allocate a specific key for a process shared by all threads. +pub unsafe fn sys_pthread_key_create( + key: *mut ctypes::pthread_key_t, + destr_function: Option, +) -> c_int { + debug!("sys_pthread_key_create <= {:#x}", key as usize); + syscall_body!(sys_pthread_key_create, { + if let Some(k) = axtask::current().alloc_key(destr_function) { + unsafe { + *key = k as ctypes::pthread_key_t; + } + Ok(0) + } else { + Err(LinuxError::EAGAIN) + } + }) +} + +/// Destroy a specific key for a process. +pub fn sys_pthread_key_delete(key: ctypes::pthread_key_t) -> c_int { + debug!("sys_pthread_key_delete <= {}", key); + syscall_body!(sys_pthread_key_delete, { + if let Some(_) = axtask::current().free_key(key as usize) { + Ok(0) + } else { + Err(LinuxError::EINVAL) + } + }) +} + +/// Set the value of a specific key for a thread. +pub fn sys_pthread_setspecific(key: ctypes::pthread_key_t, value: *const c_void) -> c_int { + debug!("sys_pthread_setspecific <= {}, {:#x}", key, value as usize); + syscall_body!(sys_pthread_setspecific, { + if let Some(_) = axtask::current().set_tsd(key as usize, value as *mut c_void) { + Ok(0) + } else { + Err(LinuxError::EINVAL) + } + }) +} + +/// Get the value of a specific key for a thread. +pub fn sys_pthread_getspecific(key: ctypes::pthread_key_t) -> *mut c_void { + debug!("sys_pthread_getspecific <= {}", key); + syscall_body!(sys_pthread_getspecific, { + if let Some(tsd) = axtask::current().get_tsd(key as usize) { + Ok(tsd) + } else { + // return null + Ok(core::ptr::null_mut()) + } + }) +} diff --git a/api/arceos_posix_api/src/lib.rs b/api/arceos_posix_api/src/lib.rs index c78e95713..b5c8dca32 100644 --- a/api/arceos_posix_api/src/lib.rs +++ b/api/arceos_posix_api/src/lib.rs @@ -74,13 +74,18 @@ pub use imp::net::{ pub use imp::pipe::sys_pipe; #[cfg(feature = "multitask")] pub use imp::pthread::condvar::{ - sys_pthread_cond_broadcast, sys_pthread_cond_init, sys_pthread_cond_signal, - sys_pthread_cond_wait, + sys_pthread_cond_broadcast, sys_pthread_cond_destroy, sys_pthread_cond_init, + sys_pthread_cond_signal, sys_pthread_cond_timedwait, sys_pthread_cond_wait, }; #[cfg(feature = "multitask")] pub use imp::pthread::mutex::{ - sys_pthread_mutex_init, sys_pthread_mutex_lock, sys_pthread_mutex_trylock, - sys_pthread_mutex_unlock, + sys_pthread_mutex_destroy, sys_pthread_mutex_init, sys_pthread_mutex_lock, + sys_pthread_mutex_trylock, sys_pthread_mutex_unlock, +}; +#[cfg(feature = "multitask")] +pub use imp::pthread::tsd::{ + sys_pthread_getspecific, sys_pthread_key_create, sys_pthread_key_delete, + sys_pthread_setspecific, }; #[cfg(feature = "multitask")] pub use imp::pthread::{sys_pthread_create, sys_pthread_exit, sys_pthread_join, sys_pthread_self}; diff --git a/apps/c/pthread/basic/expect_info_smp4_fifo.out b/apps/c/pthread/basic/expect_info_smp4_fifo.out index d6383cba7..1d7b73b73 100644 --- a/apps/c/pthread/basic/expect_info_smp4_fifo.out +++ b/apps/c/pthread/basic/expect_info_smp4_fifo.out @@ -24,7 +24,14 @@ test_create_exit: Exit message test_mutex: data = 100 Second want to continue,but need to wait A=1 Second: A is 0 -First work, Change A --> 1 and wakeup Second +First work, Change A --> 1 and wakeup Second or Third A is 1, Second can work now +Third want to continue,but need to wait A=1 +Third: A is 0, awake count: 1 +Third: A is 0, awake count: 2 +Third: A is 0, awake count: 3 +First work, Change A --> 1 and wakeup Second or Third +Third: pthread_cond_timedwait success +A is 1, Third can work now (C)Pthread basic tests run OK! Shutting down... diff --git a/apps/c/pthread/basic/features.txt b/apps/c/pthread/basic/features.txt index 6ca9b0c5f..035f5582a 100644 --- a/apps/c/pthread/basic/features.txt +++ b/apps/c/pthread/basic/features.txt @@ -1,3 +1,4 @@ alloc paging multitask +irq diff --git a/apps/c/pthread/basic/main.c b/apps/c/pthread/basic/main.c index 3db1c5462..afea61c41 100644 --- a/apps/c/pthread/basic/main.c +++ b/apps/c/pthread/basic/main.c @@ -3,8 +3,9 @@ * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A + * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ #include @@ -133,13 +134,12 @@ int A = 0; void *first(void *arg) { sleep(5); - puts("First work, Change A --> 1 and wakeup Second"); + puts("First work, Change A --> 1 and wakeup Second or Third"); pthread_mutex_lock(&lock); A = 1; pthread_cond_signal(&condvar); pthread_mutex_unlock(&lock); return NULL; - } void *second(void *arg) @@ -155,6 +155,30 @@ void *second(void *arg) return NULL; } +void *third(void *arg) +{ + struct timespec ts; + ts.tv_nsec = 0; + puts("Third want to continue,but need to wait A=1"); + pthread_mutex_lock(&lock); + int cnt = 0; + while (A == 0) { + cnt++; + printf("Third: A is %d, awake count: %d\n", A, cnt); + ts.tv_sec = time(NULL) + 2; + pthread_cond_timedwait(&condvar, &lock, &ts); + } + // condvar should be signaled three times for three 2s intervals in 5s total + if (cnt != 3) { + puts("Third: pthread_cond_timedwait fail"); + } else { + puts("Third: pthread_cond_timedwait success"); + } + printf("A is %d, Third can work now\n", A); + pthread_mutex_unlock(&lock); + return NULL; +} + void test_condvar() { pthread_t t1, t2; @@ -165,6 +189,18 @@ void test_condvar() pthread_join(t1, NULL); pthread_join(t2, NULL); + pthread_cond_destroy(&condvar); + + A = 0; + + pthread_cond_init(&condvar, NULL); + + pthread_create(&t1, NULL, first, NULL); + pthread_create(&t2, NULL, third, NULL); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + pthread_cond_destroy(&condvar); } int main() @@ -175,7 +211,9 @@ int main() test_create_join(); test_create_exit(); test_mutex(); - test_condvar(); + test_condvar(); + pthread_mutex_destroy(&lock); + puts("(C)Pthread basic tests run OK!"); return 0; diff --git a/apps/c/pthread/tsd/expect_info_smp4_fifo.out b/apps/c/pthread/tsd/expect_info_smp4_fifo.out new file mode 100644 index 000000000..c54590cf3 --- /dev/null +++ b/apps/c/pthread/tsd/expect_info_smp4_fifo.out @@ -0,0 +1,22 @@ +smp = 4 +build_mode = release +log_level = info + +CPU 0 started +Found physcial memory regions: + .text (READ | EXECUTE | RESERVED) + .rodata (READ | RESERVED) + .data .tdata .tbss .percpu (READ | WRITE | RESERVED) + .percpu (READ | WRITE | RESERVED) + boot stack (READ | WRITE | RESERVED) + .bss (READ | WRITE | RESERVED) + free memory (READ | WRITE | FREE) +Initialize global memory allocator... +Initialize kernel page table... +Initialize platform devices... +Initialize scheduling... + use FIFO scheduler. +max_keys = 1024, got No.0 +TSD test success +(C)Pthread TSD tests run OK! +Shutting down... diff --git a/apps/c/pthread/tsd/features.txt b/apps/c/pthread/tsd/features.txt new file mode 100644 index 000000000..035f5582a --- /dev/null +++ b/apps/c/pthread/tsd/features.txt @@ -0,0 +1,4 @@ +alloc +paging +multitask +irq diff --git a/apps/c/pthread/tsd/main.c b/apps/c/pthread/tsd/main.c new file mode 100644 index 000000000..4ada157a6 --- /dev/null +++ b/apps/c/pthread/tsd/main.c @@ -0,0 +1,74 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Rukos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A + * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. + */ + +#include +#include +#include +#include +#include + +static pthread_key_t p_key; + +void *specific_func(void *arg) +{ + int *p = (int *)malloc(sizeof(int)); + *p = *(int *)arg; + pthread_setspecific(p_key, p); + sleep(1); + int *tmp = (int *)pthread_getspecific(p_key); + assert(*tmp == *(int *)arg); + assert(pthread_getspecific(999999) == NULL); + return NULL; +} + +int res = 0; + +void destr_func(void *arg) +{ + res += *(int *)arg; + free(arg); + // It seems that printing in destr_func will cause deadlock + // char *buf[100]; + // sprintf(buf, "destr_func: %d", *(int *)arg); + // puts(buf); +} + +void test_specific() +{ + int max_keys = sysconf(_SC_THREAD_KEYS_MAX); + pthread_key_create(&p_key, destr_func); + printf("max_keys = %d, got No.%d\n", max_keys, p_key); + + pthread_t t1, t2; + int arg1 = 0x1234, arg2 = 0x5678; + pthread_create(&t1, NULL, specific_func, &arg1); + pthread_create(&t2, NULL, specific_func, &arg2); + pthread_join(t1, NULL); + pthread_join(t2, NULL); + if (res != 0x1234 + 0x5678) { + puts("TSD test fail"); + } else { + puts("TSD test success"); + } + + pthread_key_delete(p_key); +} + +int main() +{ + pthread_t main_thread = pthread_self(); + assert(main_thread != 0); + + test_specific(); + + puts("(C)Pthread TSD tests run OK!"); + + return 0; +} diff --git a/apps/c/pthread/tsd/test_cmd b/apps/c/pthread/tsd/test_cmd new file mode 100644 index 000000000..fa3dfed71 --- /dev/null +++ b/apps/c/pthread/tsd/test_cmd @@ -0,0 +1,2 @@ +test_one "SMP=4 LOG=info" "expect_info_smp4_fifo.out" +rm -f $APP/*.o diff --git a/modules/axconfig/defconfig.toml b/modules/axconfig/defconfig.toml index e13488d9e..0bb9ebc03 100644 --- a/modules/axconfig/defconfig.toml +++ b/modules/axconfig/defconfig.toml @@ -39,3 +39,6 @@ ticks-per-sec = "100" # Number of CPUs smp = "1" + +# Maximum number of keys per thread. +pthread-key-max = "1024" diff --git a/modules/axtask/src/api.rs b/modules/axtask/src/api.rs index 640eaedc6..f31f8e30e 100644 --- a/modules/axtask/src/api.rs +++ b/modules/axtask/src/api.rs @@ -15,6 +15,7 @@ pub(crate) use crate::run_queue::{AxRunQueue, RUN_QUEUE}; #[doc(cfg(feature = "multitask"))] pub use crate::task::{CurrentTask, TaskId, TaskInner}; +use crate::tsd; #[doc(cfg(feature = "multitask"))] pub use crate::wait_queue::WaitQueue; @@ -77,6 +78,7 @@ pub fn init_scheduler() { crate::run_queue::init(); #[cfg(feature = "irq")] crate::timers::init(); + tsd::init(); info!(" use {} scheduler.", Scheduler::scheduler_name()); } diff --git a/modules/axtask/src/lib.rs b/modules/axtask/src/lib.rs index 567f21ca9..8b8ee7c2b 100644 --- a/modules/axtask/src/lib.rs +++ b/modules/axtask/src/lib.rs @@ -51,6 +51,8 @@ cfg_if::cfg_if! { #[cfg(feature = "irq")] /// load average pub mod loadavg; + /// specific key-value storage for each task + pub mod tsd; /// TODO: if irq is disabled, what value should AVENRUN be? /// average run load, same as in linux kernel static mut AVENRUN: [u64; 3] = [0, 0, 0]; diff --git a/modules/axtask/src/run_queue.rs b/modules/axtask/src/run_queue.rs index e90c9c802..db42ffe32 100644 --- a/modules/axtask/src/run_queue.rs +++ b/modules/axtask/src/run_queue.rs @@ -101,6 +101,7 @@ impl AxRunQueue { EXITED_TASKS.lock().clear(); axhal::misc::terminate(); } else { + curr.destroy_keys(); curr.set_state(TaskState::Exited); curr.notify_exit(exit_code, self); EXITED_TASKS.lock().push_back(curr.clone()); diff --git a/modules/axtask/src/task.rs b/modules/axtask/src/task.rs index b7b91da80..65fac17a5 100644 --- a/modules/axtask/src/task.rs +++ b/modules/axtask/src/task.rs @@ -8,9 +8,11 @@ */ use alloc::{boxed::Box, string::String, sync::Arc}; +use core::ffi::c_void; use core::ops::Deref; use core::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, AtomicU8, Ordering}; use core::{alloc::Layout, cell::UnsafeCell, fmt, ptr::NonNull}; +use spinlock::SpinNoIrq; #[cfg(feature = "preempt")] use core::sync::atomic::AtomicUsize; @@ -21,6 +23,7 @@ use axhal::tls::TlsArea; use axhal::arch::TaskContext; use memory_addr::{align_up_4k, VirtAddr}; +use crate::tsd::{DestrFunction, KEYS, TSD}; use crate::{AxRunQueue, AxTask, AxTaskRef, WaitQueue}; /// A unique identifier for a thread. @@ -64,6 +67,8 @@ pub struct TaskInner { #[cfg(feature = "tls")] tls: TlsArea, + + tsd: TSD, } impl TaskId { @@ -143,6 +148,7 @@ impl TaskInner { ctx: UnsafeCell::new(TaskContext::new()), #[cfg(feature = "tls")] tls: TlsArea::alloc(), + tsd: SpinNoIrq::new([core::ptr::null_mut(); axconfig::PTHREAD_KEY_MAX]), } } @@ -292,6 +298,38 @@ impl TaskInner { } } +impl TaskInner { + /// Allocate a key + pub fn alloc_key(&self, destr_function: Option) -> Option { + unsafe { KEYS.lock() }.alloc(destr_function) + } + /// Get the destructor function of a key + pub fn free_key(&self, key: usize) -> Option<()> { + unsafe { KEYS.lock() }.free(key) + } + /// Get the destructor function of a key + pub fn set_tsd(&self, key: usize, value: *mut c_void) -> Option<()> { + if key < self.tsd.lock().len() { + self.tsd.lock()[key] = value; + Some(()) + } else { + None + } + } + /// Get the destructor function of a key + pub fn get_tsd(&self, key: usize) -> Option<*mut c_void> { + if key < self.tsd.lock().len() { + Some(self.tsd.lock()[key]) + } else { + None + } + } + /// Get the destructor function of a key + pub fn destroy_keys(&self) { + unsafe { KEYS.lock() }.destr_used_keys(&self.tsd) + } +} + impl fmt::Debug for TaskInner { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("TaskInner") diff --git a/modules/axtask/src/tsd.rs b/modules/axtask/src/tsd.rs new file mode 100644 index 000000000..970c7af86 --- /dev/null +++ b/modules/axtask/src/tsd.rs @@ -0,0 +1,122 @@ +/* Copyright (c) [2023] [Syswonder Community] + * [Rukos] is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use core::{ + ffi::c_void, + mem::MaybeUninit, + sync::atomic::{AtomicBool, AtomicPtr}, +}; +use lazy_init::LazyInit; +use spinlock::SpinNoIrq; + +/// Destroy a specific key when a thread exits. +pub type DestrFunction = unsafe extern "C" fn(*mut c_void); +/// Thread-specific data set. +pub(crate) type TSD = SpinNoIrq<[*mut c_void; axconfig::PTHREAD_KEY_MAX]>; + +/// A key for a process. +#[derive(Default)] +#[allow(dead_code)] +pub(crate) struct PthreadKey { + in_use: AtomicBool, + // AtomicPtr means *mut c_void. It should be convert to DestrFunction when use. + destr_function: AtomicPtr, +} + +impl PthreadKey { + /// Create a new key. + pub fn new() -> Self { + Self { + in_use: AtomicBool::new(false), + destr_function: AtomicPtr::new(core::ptr::null_mut()), + } + } +} + +/// A set of keys for a process. +pub(crate) struct PthreadKeys { + keys: [PthreadKey; axconfig::PTHREAD_KEY_MAX], +} + +impl PthreadKeys { + /// Create a new key set. + pub fn new() -> Self { + let mut arr: [MaybeUninit; axconfig::PTHREAD_KEY_MAX] = + unsafe { MaybeUninit::uninit().assume_init() }; + for a in arr.iter_mut() { + *a = MaybeUninit::new(PthreadKey::new()); + } + Self { + keys: unsafe { + core::mem::transmute::<_, [PthreadKey; axconfig::PTHREAD_KEY_MAX]>(arr) + }, + } + } + + /// Allocate a key + pub fn alloc(&self, destr_function: Option) -> Option { + for (i, key) in self.keys.iter().enumerate() { + if !key.in_use.load(core::sync::atomic::Ordering::Relaxed) { + key.in_use + .store(true, core::sync::atomic::Ordering::Relaxed); + if let Some(destr_function) = destr_function { + key.destr_function.store( + destr_function as *mut c_void, + core::sync::atomic::Ordering::Relaxed, + ); + } else { + key.destr_function + .store(core::ptr::null_mut(), core::sync::atomic::Ordering::Relaxed); + } + return Some(i); + } + } + None + } + + /// Free a key + pub fn free(&self, key: usize) -> Option<()> { + if key < self.keys.len() { + self.keys[key] + .in_use + .store(false, core::sync::atomic::Ordering::Relaxed); + Some(()) + } else { + None + } + } + + /// Get all keys used + pub fn destr_used_keys(&self, tsd: &TSD) { + for (i, key) in self.keys.iter().enumerate() { + if key.in_use.load(core::sync::atomic::Ordering::Relaxed) { + let destr_function = key + .destr_function + .load(core::sync::atomic::Ordering::Relaxed); + if !destr_function.is_null() { + unsafe { + let destr_function = + core::mem::transmute::<*mut c_void, DestrFunction>(destr_function); + destr_function(tsd.lock()[i]); + } + } + } + } + } +} + +/// Instance of a thread-shared key set. +pub(crate) static mut KEYS: LazyInit> = LazyInit::new(); + +/// Initialize the thread-shared key set. +pub(crate) fn init() { + unsafe { + KEYS.init_by(SpinNoIrq::new(PthreadKeys::new())); + } +} diff --git a/modules/axtask/src/wait_queue.rs b/modules/axtask/src/wait_queue.rs index 84e4c1729..1fdc6fdcd 100644 --- a/modules/axtask/src/wait_queue.rs +++ b/modules/axtask/src/wait_queue.rs @@ -105,16 +105,15 @@ impl WaitQueue { /// Blocks the current task and put it into the wait queue, until other tasks /// notify it, or the given duration has elapsed. - #[cfg(feature = "irq")] - pub fn wait_timeout(&self, dur: core::time::Duration) -> bool { + pub fn wait_timeout(&self, _deadline: core::time::Duration) -> bool { let curr = crate::current(); - let deadline = axhal::time::current_time() + dur; debug!( "task wait_timeout: {} deadline={:?}", curr.id_name(), - deadline + _deadline ); - crate::timers::set_alarm_wakeup(deadline, curr.clone()); + #[cfg(feature = "irq")] + crate::timers::set_alarm_wakeup(_deadline, curr.clone()); RUN_QUEUE.lock().block_current(|task| { task.set_in_wait_queue(true); diff --git a/ulib/axlibc/include/limits.h b/ulib/axlibc/include/limits.h index 89cafd535..4046031fe 100644 --- a/ulib/axlibc/include/limits.h +++ b/ulib/axlibc/include/limits.h @@ -41,4 +41,6 @@ #define SSIZE_MAX LONG_MAX #define CHAR_MAX 127 +#define PTHREAD_KEYS_MAX 1024 + #endif diff --git a/ulib/axlibc/include/pthread.h b/ulib/axlibc/include/pthread.h index 78fed2661..c7c6b9861 100644 --- a/ulib/axlibc/include/pthread.h +++ b/ulib/axlibc/include/pthread.h @@ -3,8 +3,9 @@ * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A + * PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. */ #ifndef _PTHREAD_H @@ -24,8 +25,8 @@ typedef struct { unsigned __attr; } pthread_condattr_t; -#include #include +#include typedef struct { unsigned __attr; } pthread_mutexattr_t; @@ -46,6 +47,9 @@ typedef void *pthread_t; #define PTHREAD_CANCELED ((void *)-1) #define SIGCANCEL 33 +/* Keys for thread-specific data */ +typedef unsigned int pthread_key_t; + #ifdef AX_CONFIG_MULTITASK _Noreturn void pthread_exit(void *); @@ -61,6 +65,7 @@ void pthread_testcancel(void); int pthread_cancel(pthread_t); int pthread_mutex_init(pthread_mutex_t *__restrict, const pthread_mutexattr_t *__restrict); +int pthread_mutex_destroy(pthread_mutex_t *); int pthread_mutex_lock(pthread_mutex_t *); int pthread_mutex_unlock(pthread_mutex_t *); int pthread_mutex_trylock(pthread_mutex_t *); @@ -69,7 +74,11 @@ int pthread_setname_np(pthread_t, const char *); int pthread_cond_init(pthread_cond_t *__restrict__ __cond, const pthread_condattr_t *__restrict__ __cond_attr); +int pthread_cond_destroy(pthread_cond_t *__cond); int pthread_cond_signal(pthread_cond_t *__cond); +int pthread_cond_timedwait(pthread_cond_t *__restrict__ __cond, + pthread_mutex_t *__restrict__ __mutex, + const struct timespec *__restrict__ __abstime); int pthread_cond_wait(pthread_cond_t *__restrict__ __cond, pthread_mutex_t *__restrict__ __mutex); int pthread_cond_broadcast(pthread_cond_t *); @@ -78,6 +87,23 @@ int pthread_attr_getstacksize(const pthread_attr_t *__restrict__ __attr, size_t *__restrict__ __stacksize); int pthread_attr_setstacksize(pthread_attr_t *__attr, size_t __stacksize); +/* Create a key value identifying a location in the thread-specific + data area. Each thread maintains a distinct thread-specific data + area. DESTR_FUNCTION, if non-NULL, is called with the value + associated to that key when the key is destroyed. + DESTR_FUNCTION is not called if the value associated is NULL when + the key is destroyed. */ +int pthread_key_create(pthread_key_t *__key, void (*__destr_function)(void *)); + +/* Destroy KEY. */ +int pthread_key_delete(pthread_key_t __key); + +/* Return current value of the thread-specific data slot identified by KEY. */ +void *pthread_getspecific(pthread_key_t __key); + +/* Store POINTER in the thread-specific data slot identified by KEY. */ +int pthread_setspecific(pthread_key_t __key, const void *__pointer); + #endif // AX_CONFIG_MULTITASK #endif // _PTHREAD_H diff --git a/ulib/axlibc/src/pthread.rs b/ulib/axlibc/src/pthread.rs index 2bf0134ea..251cc0b31 100644 --- a/ulib/axlibc/src/pthread.rs +++ b/ulib/axlibc/src/pthread.rs @@ -55,6 +55,12 @@ pub unsafe extern "C" fn pthread_mutex_init( e(api::sys_pthread_mutex_init(mutex, attr)) } +/// Destroy a mutex. +#[no_mangle] +pub unsafe extern "C" fn pthread_mutex_destroy(mutex: *mut ctypes::pthread_mutex_t) -> c_int { + e(api::sys_pthread_mutex_destroy(mutex)) +} + /// Lock the given mutex. #[no_mangle] pub unsafe extern "C" fn pthread_mutex_lock(mutex: *mut ctypes::pthread_mutex_t) -> c_int { @@ -83,6 +89,22 @@ pub unsafe extern "C" fn pthread_cond_init( e(api::sys_pthread_cond_init(condvar, attr)) } +/// Destroy a condition variable +#[no_mangle] +pub unsafe extern "C" fn pthread_cond_destroy(condvar: *mut ctypes::pthread_cond_t) -> c_int { + e(api::sys_pthread_cond_destroy(condvar)) +} + +#[no_mangle] +/// Wait for the condition variable to be signaled or timeout +pub unsafe extern "C" fn pthread_cond_timedwait( + condvar: *mut ctypes::pthread_cond_t, + mutex: *mut ctypes::pthread_mutex_t, + abstime: *const ctypes::timespec, +) -> c_int { + e(api::sys_pthread_cond_timedwait(condvar, mutex, abstime)) +} + /// Wait for the condition variable to be signaled #[no_mangle] pub unsafe extern "C" fn pthread_cond_wait( @@ -103,3 +125,33 @@ pub unsafe extern "C" fn pthread_cond_signal(condvar: *mut ctypes::pthread_cond_ pub unsafe extern "C" fn pthread_cond_broadcast(condvar: *mut ctypes::pthread_cond_t) -> c_int { e(api::sys_pthread_cond_broadcast(condvar)) } + +/// Initialize a thread-specific data key +#[no_mangle] +pub unsafe extern "C" fn pthread_key_create( + key: *mut ctypes::pthread_key_t, + dtor: Option, +) -> c_int { + e(api::sys_pthread_key_create(key, dtor)) +} + +/// Destroy a thread-specific data key +#[no_mangle] +pub unsafe extern "C" fn pthread_key_delete(key: ctypes::pthread_key_t) -> c_int { + e(api::sys_pthread_key_delete(key)) +} + +/// Get the value of a thread-specific data key +#[no_mangle] +pub unsafe extern "C" fn pthread_getspecific(key: ctypes::pthread_key_t) -> *mut c_void { + api::sys_pthread_getspecific(key) +} + +/// Set the value of a thread-specific data key +#[no_mangle] +pub unsafe extern "C" fn pthread_setspecific( + key: ctypes::pthread_key_t, + value: *const c_void, +) -> c_int { + e(api::sys_pthread_setspecific(key, value)) +} diff --git a/ulib/axlibc/src/sys.rs b/ulib/axlibc/src/sys.rs index b43358ea7..5f50428b3 100644 --- a/ulib/axlibc/src/sys.rs +++ b/ulib/axlibc/src/sys.rs @@ -42,6 +42,8 @@ pub unsafe extern "C" fn sysconf(name: c_int) -> c_long { sys_getrlimit(ctypes::RLIMIT_NOFILE.try_into().unwrap(), &mut rl); rl.rlim_max as c_long } + // Maximum number of keys per thread + ctypes::_SC_THREAD_KEYS_MAX => config::PTHREAD_KEY_MAX as c_long, _ => 0, } }