Skip to content

Commit 93cd5b0

Browse files
committed
Fix reference counting in DispatchSemaphoreGuard::release
1 parent 09f6049 commit 93cd5b0

File tree

1 file changed

+36
-2
lines changed

1 file changed

+36
-2
lines changed

crates/dispatch2/src/semaphore.rs

Lines changed: 36 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,33 @@ 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+
// SAFETY: semaphore is valid and the method signature is correct.
69+
unsafe { msg_send![semaphore, retainCount] }
70+
}
71+
72+
#[test]
73+
fn acquire_release() {
74+
let semaphore = DispatchSemaphore::new(1);
75+
76+
assert_eq!(retain_count(&semaphore), 1);
77+
{
78+
let _guard = semaphore.try_acquire(DispatchTime::NOW).unwrap();
79+
assert_eq!(retain_count(&semaphore), 2);
80+
}
81+
assert_eq!(retain_count(&semaphore), 1);
82+
{
83+
let guard = semaphore.try_acquire(DispatchTime::NOW).unwrap();
84+
assert_eq!(retain_count(&semaphore), 2);
85+
guard.release();
86+
assert_eq!(retain_count(&semaphore), 1);
87+
}
88+
assert_eq!(retain_count(&semaphore), 1);
89+
}
90+
}

0 commit comments

Comments
 (0)