From efb44d105dd6999003da2d443c26b68d25f4c768 Mon Sep 17 00:00:00 2001 From: ljy9810 Date: Thu, 29 Aug 2024 15:43:36 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E8=BF=87hold=5Ftime.rs=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=B8=AD=E7=9A=84HoldTime=E7=BB=93=E6=9E=84=E4=BD=93?= =?UTF-8?q?=EF=BC=8C=E5=AE=9E=E7=8E=B0=E5=AF=B9=E6=89=80=E5=BE=97=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=97=B6=E9=97=B4=EF=BC=8C=E8=AE=BE=E7=BD=AE=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E6=97=B6=E9=97=B4=EF=BC=8C=E9=87=8D=E7=BD=AE=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E5=92=8Cnew()=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E5=AE=8C=E6=88=90=E4=BA=92=E6=96=A5=E9=94=81=E5=92=8C?= =?UTF-8?q?=E5=85=B1=E4=BA=AB=E9=94=81=E7=9A=84=E6=8C=81=E9=94=81=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ylong_runtime/src/sync/hold_time.rs | 63 ++++++++++++ ylong_runtime/src/sync/mod.rs | 1 + ylong_runtime/src/sync/mutex.rs | 96 ++++++++++++++++++- ylong_runtime/src/sync/rwlock.rs | 142 +++++++++++++++++++++++++++- ylong_runtime/src/sync/wake_list.rs | 4 +- ylong_runtime/tests/sync.rs | 117 ++++++++++++++++++++++- 6 files changed, 410 insertions(+), 13 deletions(-) create mode 100644 ylong_runtime/src/sync/hold_time.rs diff --git a/ylong_runtime/src/sync/hold_time.rs b/ylong_runtime/src/sync/hold_time.rs new file mode 100644 index 0000000..fe46ece --- /dev/null +++ b/ylong_runtime/src/sync/hold_time.rs @@ -0,0 +1,63 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Time of mutex lock + +use std::sync::atomic::Ordering::Acquire; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::{Duration, Instant}; + +pub(crate) struct HoldTime { + hold_time: std::sync::Mutex>, + hold_num: AtomicUsize, +} + +impl HoldTime { + pub(crate) fn new() -> HoldTime { + HoldTime { + hold_time: std::sync::Mutex::new(None), + hold_num: AtomicUsize::new(0), + } + } + + pub(crate) fn get_hold_time(&self) -> Option { + let binding = self.hold_time.lock().unwrap(); + let t = binding.as_ref(); + match t { + Some(_) => t.map(|t| t.elapsed()), + None => None, + } + } + + pub(crate) fn init_hold_time(&self) { + if self.hold_num.load(Ordering::SeqCst) == 0 { + let mutex_hold_time = &self.hold_time; + *mutex_hold_time.lock().unwrap() = Some(Instant::now()); + } + } + + pub(crate) fn drop_hold_time(&self) { + if self.hold_num.load(Ordering::SeqCst) == 0 { + let mutex_hold_time = &self.hold_time; + *mutex_hold_time.lock().unwrap() = None; + } + } + + pub(crate) fn hold_num_add(&self) { + self.hold_num.fetch_add(1, Acquire); + } + + pub(crate) fn hold_num_sub(&self) { + self.hold_num.fetch_sub(1, Acquire); + } +} diff --git a/ylong_runtime/src/sync/mod.rs b/ylong_runtime/src/sync/mod.rs index 4c9d269..0e930f4 100644 --- a/ylong_runtime/src/sync/mod.rs +++ b/ylong_runtime/src/sync/mod.rs @@ -21,6 +21,7 @@ pub mod oneshot; pub mod rwlock; pub mod semaphore; pub(crate) mod semaphore_inner; +mod hold_time; pub mod waiter; mod wake_list; pub mod watch; diff --git a/ylong_runtime/src/sync/mutex.rs b/ylong_runtime/src/sync/mutex.rs index 46df16a..33c178e 100644 --- a/ylong_runtime/src/sync/mutex.rs +++ b/ylong_runtime/src/sync/mutex.rs @@ -13,13 +13,14 @@ //! Mutual exclusion locks +use crate::sync::hold_time::HoldTime; +use crate::sync::semaphore_inner::SemaphoreInner; use std::cell::UnsafeCell; use std::error::Error; use std::fmt; use std::fmt::{Display, Formatter}; use std::ops::{Deref, DerefMut}; - -use crate::sync::semaphore_inner::SemaphoreInner; +use std::time::Duration; /// An async version of [`std::sync::Mutex`] /// @@ -35,6 +36,8 @@ use crate::sync::semaphore_inner::SemaphoreInner; pub struct Mutex { /// Semaphore to provide mutual exclusion sem: SemaphoreInner, + /// The duration of locking the mutex + hold_time: HoldTime, /// The data protected by this mutex data: UnsafeCell, } @@ -68,6 +71,7 @@ impl Mutex { Mutex { // bounded by permit::max sem: SemaphoreInner::new(1).unwrap(), + hold_time: HoldTime::new(), data: UnsafeCell::new(t), } } @@ -97,7 +101,9 @@ impl Mutex { // The result of `acquire()` will be `Err()` only when the semaphore is closed. // `Mutex` will not close, so the result of `acquire()` must be `Ok(())`. self.sem.acquire().await.unwrap(); - MutexGuard(self) + let m_g = MutexGuard(self); + self.init_hold_time(); + m_g } /// Attempts to get the mutex. @@ -122,7 +128,11 @@ impl Mutex { /// ``` pub fn try_lock(&self) -> Result, LockError> { match self.sem.try_acquire() { - Ok(_) => Ok(MutexGuard(self)), + Ok(_) => { + let r_g = MutexGuard(self); + self.init_hold_time(); + Ok(r_g) + } Err(_) => Err(LockError), } } @@ -136,6 +146,34 @@ impl Mutex { pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *self.data.get() } } + + fn init_hold_time(&self) { + self.hold_time.init_hold_time(); + self.hold_time.hold_num_add(); + } + + /// Gets the time of the mutex has been obtained. + /// + /// If the mutex is not locked,it will return a None. Otherwise, the + /// duration will be returned. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// + /// use ylong_runtime::sync::mutex::Mutex; + /// + /// let mutex = Mutex::new(2); + /// assert_eq!(mutex.get_hold_time(), None); + /// let lock = mutex.try_lock(); + /// thread::sleep(Duration::from_millis(2)); + /// assert!(mutex.get_hold_time().unwrap().as_millis() >= 2); + /// ``` + pub fn get_hold_time(&self) -> Option { + self.hold_time.get_hold_time() + } } /// Mutex guard to access the data after holding the mutex. @@ -146,6 +184,12 @@ impl MutexGuard<'_, T> { fn unlock(&mut self) { self.0.sem.release(); } + + // Reset time to None + fn drop_hold_time(&self) { + self.0.hold_time.hold_num_sub(); + self.0.hold_time.drop_hold_time(); + } } unsafe impl Sync for MutexGuard<'_, T> {} @@ -153,6 +197,7 @@ unsafe impl Sync for MutexGuard<'_, T> {} /// The mutex will be released after the mutex guard is dropped. impl Drop for MutexGuard<'_, T> { fn drop(&mut self) { + self.drop_hold_time(); self.unlock(); } } @@ -286,4 +331,47 @@ mod tests { assert!(mutex.try_lock().is_ok()); }); } + + ///UT test cases for Mutex::get_hold_time() interface + /// + /// #Brief + /// 1. Creating a Concurrent Mutual Exclusion Lock + /// 2. Verify the initial time of the lock + /// 3. Perform locking operation + /// 4. Blocking threads + /// 5. Verify the holding time of the lock + #[test] + fn ut_mutex_hold_time_01() { + let mutex = Mutex::new(10); + assert_eq!(mutex.get_hold_time(), None); + block_on(async { + let _lock = mutex.lock().await; + thread::sleep(Duration::from_millis(10)); + let hold_time = mutex.get_hold_time().unwrap().as_millis(); + assert!(hold_time >= 10, "mutex_hold_time: {} should_hold_time: 10", hold_time); + }); + assert_eq!(mutex.get_hold_time(), None); + } + + ///UT test cases for Mutex::get_hold_time() interface + /// + /// #Brief + /// 1. Creating a Concurrent Mutual Exclusion Lock + /// 2. Verify the initial time of the lock + /// 3. Perform locking operation + /// 4. Blocking threads + /// 5. Verify the holding time of the lock + /// 6. drop the lock + /// 7. Verify the time after drop the lock + #[test] + fn ut_mutex_hold_time_02() { + let mutex = Mutex::new(10); + assert_eq!(mutex.get_hold_time(), None); + let lock = mutex.try_lock(); + thread::sleep(Duration::from_millis(10)); + let hold_time = mutex.get_hold_time().unwrap().as_millis(); + assert!(hold_time >= 10, "mutex_hold_time: {} should_hold_time: 10", hold_time); + drop(lock); + assert_eq!(mutex.get_hold_time(), None); + } } diff --git a/ylong_runtime/src/sync/rwlock.rs b/ylong_runtime/src/sync/rwlock.rs index 3ef1d65..f45d297 100644 --- a/ylong_runtime/src/sync/rwlock.rs +++ b/ylong_runtime/src/sync/rwlock.rs @@ -18,7 +18,9 @@ use std::fmt; use std::ops::{Deref, DerefMut}; use std::sync::atomic::AtomicI64; use std::sync::atomic::Ordering::{AcqRel, Acquire, Release}; +use std::time::Duration; +use crate::sync::hold_time::HoldTime; use crate::sync::semaphore_inner::SemaphoreInner; use crate::sync::LockError; @@ -59,6 +61,8 @@ pub struct RwLock { write_mutex: SemaphoreInner, read_count: AtomicI64, read_wait: AtomicI64, + hold_time_write: HoldTime, + hold_time_read: HoldTime, data: UnsafeCell, } @@ -84,6 +88,8 @@ impl RwLock { write_mutex: SemaphoreInner::new(1).unwrap(), read_count: AtomicI64::new(0), read_wait: AtomicI64::new(0), + hold_time_write: HoldTime::new(), + hold_time_read: HoldTime::new(), data: UnsafeCell::new(t), } } @@ -117,7 +123,9 @@ impl RwLock { // `RwLock` will not close, so the result of `acquire()` must be `Ok(())`. self.read_sem.acquire().await.unwrap(); } - RwLockReadGuard(self) + let r_g = RwLockReadGuard(self); + self.init_hold_time_read(); + r_g } /// Attempts to get the read lock. If another writer is holding the write @@ -143,7 +151,11 @@ impl RwLock { .read_count .compare_exchange(read_count, read_count + 1, AcqRel, Acquire) { - Ok(_) => return Ok(RwLockReadGuard(self)), + Ok(_) => { + let r_g = RwLockReadGuard(self); + self.init_hold_time_read(); + return Ok(r_g); + } Err(curr) => read_count = curr, } } @@ -177,7 +189,9 @@ impl RwLock { if read_count >= 0 && self.read_wait.fetch_add(read_count, Release) != -read_count { self.write_sem.acquire().await.unwrap(); } - RwLockWriteGuard(self) + let r_g = RwLockWriteGuard(self); + self.init_hold_time_write(); + r_g } /// Attempts to acquire the write lock. @@ -202,7 +216,11 @@ impl RwLock { .read_count .compare_exchange(0, -MAX_READS, AcqRel, Acquire) { - Ok(_) => Ok(RwLockWriteGuard(self)), + Ok(_) => { + let r_g = RwLockWriteGuard(self); + self.init_hold_time_write(); + Ok(r_g) + } Err(_) => { self.write_mutex.release(); Err(LockError) @@ -244,6 +262,63 @@ impl RwLock { pub fn get_mut(&mut self) -> &mut T { unsafe { &mut *self.data.get() } } + + /// + fn init_hold_time_write(&self) { + self.hold_time_write.init_hold_time(); + self.hold_time_write.hold_num_add(); + } + + fn init_hold_time_read(&self) { + self.hold_time_read.init_hold_time(); + self.hold_time_read.hold_num_add(); + } + + /// Gets the time of the RwLock has been obtained. + /// + /// If the mutex is not locked,it will return a None. Otherwise, the + /// Duration will be returned. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// + /// use ylong_runtime::sync::rwlock::RwLock; + /// + /// let rwlock = RwLock::new(2); + /// assert_eq!(rwlock.get_hold_time_write(), None); + /// let lock = rwlock.try_write(); + /// thread::sleep(Duration::from_millis(2)); + /// assert!(rwlock.get_hold_time_write().unwrap().as_millis() >= 2); + /// ``` + pub fn get_hold_time_write(&self) -> Option { + self.hold_time_write.get_hold_time() + } + + /// Gets the time of the RwLock has been obtained. + /// + /// If the mutex is not locked,it will return a None. Otherwise, the + /// Duration will be returned. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// + /// use ylong_runtime::sync::rwlock::RwLock; + /// + /// let rwlock = RwLock::new(2); + /// assert_eq!(rwlock.get_hold_time_read(), None); + /// let lock = rwlock.try_read(); + /// thread::sleep(Duration::from_millis(2)); + /// assert!(rwlock.get_hold_time_read().unwrap().as_millis() >= 2); + /// ``` + pub fn get_hold_time_read(&self) -> Option { + self.hold_time_read.get_hold_time() + } } /// Read guard to access the data after holding the mutex. @@ -262,11 +337,17 @@ impl RwLockReadGuard<'_, T> { self.0.write_sem.release(); } } + + fn drop_hold_time(&self) { + self.0.hold_time_read.hold_num_sub(); + self.0.hold_time_read.drop_hold_time(); + } } /// Unlock the read lock when ReadGuard is dropped. impl Drop for RwLockReadGuard<'_, T> { fn drop(&mut self) { + self.drop_hold_time(); self.unlock(); } } @@ -294,6 +375,12 @@ impl fmt::Display for RwLockReadGuard<'_, T> { /// RwLock write guard pub struct RwLockWriteGuard<'a, T: ?Sized>(&'a RwLock); +impl RwLockWriteGuard<'_, T> { + fn drop_hold_time(&self) { + self.0.hold_time_write.hold_num_sub(); + self.0.hold_time_write.drop_hold_time(); + } +} unsafe impl Send for RwLockWriteGuard<'_, T> {} unsafe impl Sync for RwLockWriteGuard<'_, T> {} @@ -301,6 +388,7 @@ unsafe impl Sync for RwLockWriteGuard<'_, T> {} /// is dropped. impl Drop for RwLockWriteGuard<'_, T> { fn drop(&mut self) { + self.drop_hold_time(); let read_count = self.0.read_count.fetch_add(MAX_READS, Release) + MAX_READS; self.0.read_sem.release_multi(read_count as usize); self.0.write_mutex.release(); @@ -335,6 +423,7 @@ impl DerefMut for RwLockWriteGuard<'_, T> { #[cfg(test)] mod tests { use std::sync::Arc; + use std::thread; use super::*; use crate::{block_on, spawn}; @@ -499,9 +588,9 @@ mod tests { *aa += 100; assert_eq!(*aa, 200); } - /// UT test cases for Rwlock::try_write() /// + /// /// # Brief /// 1. Creating a concurrent read/write lock /// 2. Execute command cargo test ut_rwlock_try_write_02 @@ -529,4 +618,47 @@ mod tests { let lock = RwLock::new(10); assert_eq!(lock.into_inner(), 10); } + + ///UT test cases for RwLock::get_hold_time() interface + /// + /// #Brief + /// 1. Creating a Concurrent Mutual Exclusion Lock + /// 2. Verify the initial time of the lock + /// 3. Perform locking operation + /// 4. Blocking threads + /// 5. Verify the holding time of the lock + #[test] + fn ut_rwlock_write_hold_time_01() { + let lock = RwLock::new(10); + assert_eq!(lock.get_hold_time_write(), None); + block_on(async { + let _write = lock.write().await; + thread::sleep(Duration::from_millis(10)); + let hold_time = lock.get_hold_time_write().unwrap().as_millis(); + assert!(hold_time >= 10, "lock_hold_time: {} should_hold_time: 10", hold_time); + }); + assert_eq!(lock.get_hold_time_write(), None); + } + + ///UT test cases for RwLock::get_hold_time() interface + /// + /// #Brief + /// 1. Creating a Concurrent Mutual Exclusion Lock + /// 2. Verify the initial time of the lock + /// 3. Perform locking operation + /// 4. Blocking threads + /// 5. Verify the holding time of the lock + /// 6. drop the lock + /// 7. Verify the time after drop the lock + #[test] + fn ut_rwlock_read_hold_time_01() { + let lock = RwLock::new(10); + assert_eq!(lock.get_hold_time_read(), None); + let lock_guard = lock.try_read(); + thread::sleep(Duration::from_millis(10)); + let hold_time = lock.get_hold_time_read().unwrap().as_millis(); + assert!(hold_time >= 10, "lock_hold_time: {} should_hold_time: 10", hold_time); + drop(lock_guard); + assert_eq!(lock.get_hold_time_read(), None); + } } diff --git a/ylong_runtime/src/sync/wake_list.rs b/ylong_runtime/src/sync/wake_list.rs index ac5749a..e64bc46 100644 --- a/ylong_runtime/src/sync/wake_list.rs +++ b/ylong_runtime/src/sync/wake_list.rs @@ -11,14 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::util::slots::{Slots, SlotsError}; use std::cell::UnsafeCell; use std::hint::spin_loop; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::Waker; -use crate::util::slots::{Slots, SlotsError}; - /// The first left most bit represents LOCKED state const LOCKED: usize = 1 << 0; /// The third left most bit represents NOTIFIABLE state @@ -135,7 +134,6 @@ impl Inner { pub(crate) struct Lock<'a> { waker_set: &'a WakerList, } - impl Drop for Lock<'_> { #[inline] fn drop(&mut self) { diff --git a/ylong_runtime/tests/sync.rs b/ylong_runtime/tests/sync.rs index c603d74..a1f430f 100644 --- a/ylong_runtime/tests/sync.rs +++ b/ylong_runtime/tests/sync.rs @@ -22,7 +22,6 @@ use std::task::{Context, Poll}; use std::thread; use std::thread::sleep; use std::time::Duration; - use ylong_runtime::sync::{Mutex as YlongMutex, RwLock, Waiter}; const NUM: usize = 200; @@ -483,3 +482,119 @@ fn sdv_waiter_with_wake_all() { waiter.wake_all(); } } + +/// SDV test cases for `mutex::get_hold_time()`. +/// +/// # Brief +/// 1. Multiple asynchronous tasks retrieve time. +#[test] +fn sdv_mutex_with_get_hold_time_01() { + let mut future_handlers = Vec::new(); + let mutex1 = Arc::new(YlongMutex::new(10)); + assert_eq!(mutex1.get_hold_time(), None); + for _ in 0..10 { + let mutex_nem = mutex1.clone(); + let handler = ylong_runtime::spawn(async move { + let _lock = mutex_nem.lock().await; + thread::sleep(Duration::from_millis(5)); + let hold_time = mutex_nem.get_hold_time().unwrap().as_millis(); + assert!(hold_time >= 5, "mutex_hold_time: {} should_hold_time: 5", hold_time); + }); + future_handlers.push(handler); + } + for handler in future_handlers { + let _res = ylong_runtime::block_on(handler); + } + let mut future_handlers = Vec::new(); + assert_eq!(mutex1.get_hold_time(), None); + let _lock = mutex1.try_lock(); + for _ in 0..10 { + let mutex_nem = mutex1.clone(); + let handler = ylong_runtime::spawn(async move { + thread::sleep(Duration::from_millis(5)); + let hold_time = mutex_nem.get_hold_time().unwrap().as_millis(); + assert!(hold_time >= 5, "mutex_hold_time: {} should_hold_time: 5", hold_time); + }); + future_handlers.push(handler); + } + for handler in future_handlers { + let _res = ylong_runtime::block_on(handler); + } +} + +/// SDV test cases for `RwLock::get_hold_time()`. +/// +/// # Brief +/// 1. Multiple asynchronous tasks retrieve time. +#[test] +fn sdv_rwlock_write_get_hold_time_01() { + let mut future_handlers = Vec::new(); + let rwlock = Arc::new(RwLock::new(10)); + assert_eq!(rwlock.get_hold_time_write(), None); + for _ in 0..10 { + let rwlock_new = rwlock.clone(); + let handler = ylong_runtime::spawn(async move { + let _lock = rwlock_new.write().await; + thread::sleep(Duration::from_millis(5)); + let hold_time = rwlock_new.get_hold_time_write().unwrap().as_millis(); + assert!(hold_time >= 5, "lock_hold_time: {} should_hold_time: 5", hold_time); + }); + future_handlers.push(handler); + } + for handler in future_handlers { + let _res = ylong_runtime::block_on(handler); + } + let mut future_handlers = Vec::new(); + assert_eq!(rwlock.get_hold_time_write(), None); + let _lock = rwlock.try_write(); + for _ in 0..10 { + let rwlock_new = rwlock.clone(); + let handler = ylong_runtime::spawn(async move { + thread::sleep(Duration::from_millis(5)); + let hold_time = rwlock_new.get_hold_time_write().unwrap().as_millis(); + assert!(hold_time >= 5, "lock_hold_time: {} should_hold_time: 5", hold_time); + }); + future_handlers.push(handler); + } + for handler in future_handlers { + let _res = ylong_runtime::block_on(handler); + } +} + +/// SDV test cases for `RwLock::get_hold_time()`. +/// +/// # Brief +/// 1. Multiple asynchronous tasks retrieve time. +#[test] +fn sdv_rwlock_read_get_hold_time_01() { + let mut future_handlers = Vec::new(); + let rwlock = Arc::new(RwLock::new(10)); + assert_eq!(rwlock.get_hold_time_read(), None); + for _ in 0..10 { + let rwlock_new = rwlock.clone(); + let handler = ylong_runtime::spawn(async move { + let _lock = rwlock_new.read().await; + thread::sleep(Duration::from_millis(5)); + let hold_time = rwlock_new.get_hold_time_read().unwrap().as_millis(); + assert!(hold_time >= 5, "lock_hold_time: {} should_hold_time: 5", hold_time); + }); + future_handlers.push(handler); + } + for handler in future_handlers { + let _res = ylong_runtime::block_on(handler); + } + let mut future_handlers = Vec::new(); + let _lock = rwlock.try_read(); + for _ in 0..10 { + let rwlock_new = rwlock.clone(); + let handler = ylong_runtime::spawn(async move { + thread::sleep(Duration::from_millis(5)); + let hold_time = rwlock_new.get_hold_time_read().unwrap().as_millis(); + assert!(hold_time >= 5, "lock_hold_time: {} should_hold_time: 5", hold_time); + }); + future_handlers.push(handler); + } + for handler in future_handlers { + let _res = ylong_runtime::block_on(handler); + } +} -- Gitee