From f0923b0bb85cbde2ef6eb025ec010a570e8e623b Mon Sep 17 00:00:00 2001 From: vvvviiv Date: Sun, 7 Apr 2024 22:50:11 +0800 Subject: [PATCH 1/4] sync: add `detach` method to the permit types Add `detach` method to `SemaphorePermit` and `OwnedSemaphorePermit`. --- tokio/src/sync/semaphore.rs | 40 +++++++++++++++++++++++++++++ tokio/tests/sync_semaphore.rs | 16 ++++++++++++ tokio/tests/sync_semaphore_owned.rs | 16 ++++++++++++ 3 files changed, 72 insertions(+) diff --git a/tokio/src/sync/semaphore.rs b/tokio/src/sync/semaphore.rs index a2b4074590b..db527df1305 100644 --- a/tokio/src/sync/semaphore.rs +++ b/tokio/src/sync/semaphore.rs @@ -990,6 +990,24 @@ impl<'a> SemaphorePermit<'a> { self.permits += other.permits; other.permits = 0; } + + /// Detaches `n` permits from `self` and returns a new [`SemaphorePermit`] instance that holds `n` permits. + /// + /// It guarantees at least one permit held by both `self` and the new instance. + /// + /// If there are insufficient permits and it's not possible to reduce by `n`, returns `None`. + pub fn detach(&mut self, n: u32) -> Option { + if n == 0 || n >= self.permits { + return None; + } + + self.permits -= n; + + Some(Self { + sem: self.sem, + permits: n, + }) + } } impl OwnedSemaphorePermit { @@ -1019,6 +1037,28 @@ impl OwnedSemaphorePermit { other.permits = 0; } + /// Detaches `n` permits from `self` and returns a new [`OwnedSemaphorePermit`] instance that holds `n` permits. + /// + /// It guarantees at least one permit held by both `self` and the new instance. + /// + /// If there are insufficient permits and it's not possible to reduce by `n`, returns `None`. + /// + /// # Note + /// + /// It will clone the owned `Arc` to construct the new instance. + pub fn detach(&mut self, n: u32) -> Option { + if n == 0 || n >= self.permits { + return None; + } + + self.permits -= n; + + Some(Self { + sem: self.sem.clone(), + permits: n, + }) + } + /// Returns the [`Semaphore`] from which this permit was acquired. pub fn semaphore(&self) -> &Arc { &self.sem diff --git a/tokio/tests/sync_semaphore.rs b/tokio/tests/sync_semaphore.rs index 40a5a0802a6..6a8b86cd990 100644 --- a/tokio/tests/sync_semaphore.rs +++ b/tokio/tests/sync_semaphore.rs @@ -88,6 +88,22 @@ fn merge_unrelated_permits() { p1.merge(p2); } +#[test] +fn detach() { + let sem = Semaphore::new(5); + let mut p1 = sem.try_acquire_many(3).unwrap(); + assert_eq!(sem.available_permits(), 2); + let mut p2 = p1.detach(1).unwrap(); + assert_eq!(sem.available_permits(), 2); + assert!(p1.detach(0).is_none()); + drop(p1); + assert_eq!(sem.available_permits(), 4); + assert!(p2.detach(1).is_none()); + assert!(p2.detach(2).is_none()); + drop(p2); + assert_eq!(sem.available_permits(), 5); +} + #[tokio::test] #[cfg(feature = "full")] async fn stress_test() { diff --git a/tokio/tests/sync_semaphore_owned.rs b/tokio/tests/sync_semaphore_owned.rs index d4b12d40e45..0d1d2216b64 100644 --- a/tokio/tests/sync_semaphore_owned.rs +++ b/tokio/tests/sync_semaphore_owned.rs @@ -114,6 +114,22 @@ fn merge_unrelated_permits() { p1.merge(p2) } +#[test] +fn detach() { + let sem = Arc::new(Semaphore::new(5)); + let mut p1 = sem.clone().try_acquire_many_owned(3).unwrap(); + assert_eq!(sem.available_permits(), 2); + let mut p2 = p1.detach(1).unwrap(); + assert_eq!(sem.available_permits(), 2); + assert!(p1.detach(0).is_none()); + drop(p1); + assert_eq!(sem.available_permits(), 4); + assert!(p2.detach(1).is_none()); + assert!(p2.detach(2).is_none()); + drop(p2); + assert_eq!(sem.available_permits(), 5); +} + #[tokio::test] #[cfg(feature = "full")] async fn stress_test() { From 6bdb16d1171a07e315eb4398d4ccd9396d4bbd91 Mon Sep 17 00:00:00 2001 From: vvvviiv Date: Mon, 8 Apr 2024 09:09:09 +0800 Subject: [PATCH 2/4] sync: fix fmt --- tokio/src/sync/semaphore.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tokio/src/sync/semaphore.rs b/tokio/src/sync/semaphore.rs index db527df1305..7d23de3528e 100644 --- a/tokio/src/sync/semaphore.rs +++ b/tokio/src/sync/semaphore.rs @@ -990,9 +990,9 @@ impl<'a> SemaphorePermit<'a> { self.permits += other.permits; other.permits = 0; } - + /// Detaches `n` permits from `self` and returns a new [`SemaphorePermit`] instance that holds `n` permits. - /// + /// /// It guarantees at least one permit held by both `self` and the new instance. /// /// If there are insufficient permits and it's not possible to reduce by `n`, returns `None`. @@ -1000,9 +1000,9 @@ impl<'a> SemaphorePermit<'a> { if n == 0 || n >= self.permits { return None; } - + self.permits -= n; - + Some(Self { sem: self.sem, permits: n, @@ -1042,9 +1042,9 @@ impl OwnedSemaphorePermit { /// It guarantees at least one permit held by both `self` and the new instance. /// /// If there are insufficient permits and it's not possible to reduce by `n`, returns `None`. - /// + /// /// # Note - /// + /// /// It will clone the owned `Arc` to construct the new instance. pub fn detach(&mut self, n: u32) -> Option { if n == 0 || n >= self.permits { From 3c64c0253a9bf47fa0c0a02f5ca81b46a844afad Mon Sep 17 00:00:00 2001 From: vvvviiv Date: Tue, 9 Apr 2024 23:12:30 +0800 Subject: [PATCH 3/4] sync: add `num_permits` method to the permit types Add `num_permits` method to `SemaphorePermit` and `OwnedSemaphorePermit`. --- tokio/src/sync/semaphore.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tokio/src/sync/semaphore.rs b/tokio/src/sync/semaphore.rs index 7d23de3528e..2c8b5c647f2 100644 --- a/tokio/src/sync/semaphore.rs +++ b/tokio/src/sync/semaphore.rs @@ -1008,6 +1008,11 @@ impl<'a> SemaphorePermit<'a> { permits: n, }) } + + /// Returns the number of permits held by `self`. + pub fn num_permits(&self) -> u32 { + self.permits + } } impl OwnedSemaphorePermit { @@ -1063,6 +1068,11 @@ impl OwnedSemaphorePermit { pub fn semaphore(&self) -> &Arc { &self.sem } + + /// Returns the number of permits held by `self`. + pub fn num_permits(&self) -> u32 { + self.permits + } } impl Drop for SemaphorePermit<'_> { From 34b0571af7f47c3b961c051c5113832fa04025d4 Mon Sep 17 00:00:00 2001 From: vvvviiv Date: Tue, 9 Apr 2024 23:42:58 +0800 Subject: [PATCH 4/4] sync: rename `detach` method to `split` and change its behavior --- tokio/src/sync/semaphore.rs | 16 ++++++---------- tokio/tests/sync_semaphore.rs | 20 +++++++++++++++----- tokio/tests/sync_semaphore_owned.rs | 20 +++++++++++++++----- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/tokio/src/sync/semaphore.rs b/tokio/src/sync/semaphore.rs index 2c8b5c647f2..a952729b563 100644 --- a/tokio/src/sync/semaphore.rs +++ b/tokio/src/sync/semaphore.rs @@ -991,13 +991,11 @@ impl<'a> SemaphorePermit<'a> { other.permits = 0; } - /// Detaches `n` permits from `self` and returns a new [`SemaphorePermit`] instance that holds `n` permits. - /// - /// It guarantees at least one permit held by both `self` and the new instance. + /// Splits `n` permits from `self` and returns a new [`SemaphorePermit`] instance that holds `n` permits. /// /// If there are insufficient permits and it's not possible to reduce by `n`, returns `None`. - pub fn detach(&mut self, n: u32) -> Option { - if n == 0 || n >= self.permits { + pub fn split(&mut self, n: u32) -> Option { + if n > self.permits { return None; } @@ -1042,17 +1040,15 @@ impl OwnedSemaphorePermit { other.permits = 0; } - /// Detaches `n` permits from `self` and returns a new [`OwnedSemaphorePermit`] instance that holds `n` permits. - /// - /// It guarantees at least one permit held by both `self` and the new instance. + /// Splits `n` permits from `self` and returns a new [`OwnedSemaphorePermit`] instance that holds `n` permits. /// /// If there are insufficient permits and it's not possible to reduce by `n`, returns `None`. /// /// # Note /// /// It will clone the owned `Arc` to construct the new instance. - pub fn detach(&mut self, n: u32) -> Option { - if n == 0 || n >= self.permits { + pub fn split(&mut self, n: u32) -> Option { + if n > self.permits { return None; } diff --git a/tokio/tests/sync_semaphore.rs b/tokio/tests/sync_semaphore.rs index 6a8b86cd990..ab4b316cee1 100644 --- a/tokio/tests/sync_semaphore.rs +++ b/tokio/tests/sync_semaphore.rs @@ -89,18 +89,28 @@ fn merge_unrelated_permits() { } #[test] -fn detach() { +fn split() { let sem = Semaphore::new(5); let mut p1 = sem.try_acquire_many(3).unwrap(); assert_eq!(sem.available_permits(), 2); - let mut p2 = p1.detach(1).unwrap(); + assert_eq!(p1.num_permits(), 3); + let mut p2 = p1.split(1).unwrap(); assert_eq!(sem.available_permits(), 2); - assert!(p1.detach(0).is_none()); + assert_eq!(p1.num_permits(), 2); + assert_eq!(p2.num_permits(), 1); + let p3 = p1.split(0).unwrap(); + assert_eq!(p3.num_permits(), 0); drop(p1); assert_eq!(sem.available_permits(), 4); - assert!(p2.detach(1).is_none()); - assert!(p2.detach(2).is_none()); + let p4 = p2.split(1).unwrap(); + assert_eq!(p2.num_permits(), 0); + assert_eq!(p4.num_permits(), 1); + assert!(p2.split(1).is_none()); drop(p2); + assert_eq!(sem.available_permits(), 4); + drop(p3); + assert_eq!(sem.available_permits(), 4); + drop(p4); assert_eq!(sem.available_permits(), 5); } diff --git a/tokio/tests/sync_semaphore_owned.rs b/tokio/tests/sync_semaphore_owned.rs index 0d1d2216b64..f9eeee0cfab 100644 --- a/tokio/tests/sync_semaphore_owned.rs +++ b/tokio/tests/sync_semaphore_owned.rs @@ -115,18 +115,28 @@ fn merge_unrelated_permits() { } #[test] -fn detach() { +fn split() { let sem = Arc::new(Semaphore::new(5)); let mut p1 = sem.clone().try_acquire_many_owned(3).unwrap(); assert_eq!(sem.available_permits(), 2); - let mut p2 = p1.detach(1).unwrap(); + assert_eq!(p1.num_permits(), 3); + let mut p2 = p1.split(1).unwrap(); assert_eq!(sem.available_permits(), 2); - assert!(p1.detach(0).is_none()); + assert_eq!(p1.num_permits(), 2); + assert_eq!(p2.num_permits(), 1); + let p3 = p1.split(0).unwrap(); + assert_eq!(p3.num_permits(), 0); drop(p1); assert_eq!(sem.available_permits(), 4); - assert!(p2.detach(1).is_none()); - assert!(p2.detach(2).is_none()); + let p4 = p2.split(1).unwrap(); + assert_eq!(p2.num_permits(), 0); + assert_eq!(p4.num_permits(), 1); + assert!(p2.split(1).is_none()); drop(p2); + assert_eq!(sem.available_permits(), 4); + drop(p3); + assert_eq!(sem.available_permits(), 4); + drop(p4); assert_eq!(sem.available_permits(), 5); }