Skip to content

Commit 678b5fd

Browse files
committed
Fix reference counting in DispatchSemaphoreGuard::release
1 parent 09f6049 commit 678b5fd

File tree

1 file changed

+35
-2
lines changed

1 file changed

+35
-2
lines changed

crates/dispatch2/src/semaphore.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,14 @@ pub struct DispatchSemaphoreGuard(DispatchRetained<DispatchSemaphore>);
3939
impl DispatchSemaphoreGuard {
4040
/// Release the [`DispatchSemaphore`].
4141
pub fn release(self) -> bool {
42-
let this = ManuallyDrop::new(self);
42+
// Extract the inner `DispatchRetained` so that it will be dropped independently of
43+
// `self`. SAFETY: `self.0` is a valid `DispatchRetained`.
44+
let semaphore: DispatchRetained<DispatchSemaphore> = unsafe {
45+
DispatchRetained::from_raw(DispatchRetained::as_ptr(&ManuallyDrop::new(self).0))
46+
};
4347

4448
// SAFETY: DispatchSemaphore cannot be null.
45-
let result = this.0.signal();
49+
let result = semaphore.signal();
4650

4751
result != 0
4852
}
@@ -54,3 +58,32 @@ impl Drop for DispatchSemaphoreGuard {
5458
self.0.signal();
5559
}
5660
}
61+
62+
#[cfg(test)]
63+
mod tests {
64+
use super::*;
65+
use objc2::msg_send;
66+
67+
fn retain_count(semaphore: &DispatchSemaphore) -> usize {
68+
unsafe { msg_send![semaphore, retainCount] }
69+
}
70+
71+
#[test]
72+
fn acquire_release() {
73+
let semaphore = DispatchSemaphore::new(1);
74+
75+
assert_eq!(retain_count(&semaphore), 1);
76+
{
77+
let _guard = semaphore.try_acquire(DispatchTime::NOW).unwrap();
78+
assert_eq!(retain_count(&semaphore), 2);
79+
}
80+
assert_eq!(retain_count(&semaphore), 1);
81+
{
82+
let guard = semaphore.try_acquire(DispatchTime::NOW).unwrap();
83+
assert_eq!(retain_count(&semaphore), 2);
84+
guard.release();
85+
assert_eq!(retain_count(&semaphore), 1);
86+
}
87+
assert_eq!(retain_count(&semaphore), 1);
88+
}
89+
}

0 commit comments

Comments
 (0)