@@ -26,31 +26,74 @@ impl DispatchSemaphore {
2626 let result = Self :: wait ( self , timeout) ;
2727
2828 match result {
29- 0 => Ok ( DispatchSemaphoreGuard ( self . retain ( ) ) ) ,
29+ 0 => Ok ( DispatchSemaphoreGuard ( ManuallyDrop :: new ( self . retain ( ) ) ) ) ,
3030 _ => Err ( WaitError :: Timeout ) ,
3131 }
3232 }
3333}
3434
3535/// Dispatch semaphore guard.
36+ ///
37+ /// The semaphore will be signaled when the guard is dropped, or when [`release`] is called.
38+ ///
39+ /// [`release`]: DispatchSemaphoreGuard::release
40+ //
41+ // See `release` for discussion of the `ManuallyDrop`.
3642#[ derive( Debug ) ]
37- pub struct DispatchSemaphoreGuard ( DispatchRetained < DispatchSemaphore > ) ;
43+ pub struct DispatchSemaphoreGuard ( ManuallyDrop < DispatchRetained < DispatchSemaphore > > ) ;
3844
3945impl DispatchSemaphoreGuard {
4046 /// Release the [`DispatchSemaphore`].
4147 pub fn release ( self ) -> bool {
42- let this = ManuallyDrop :: new ( self ) ;
43-
44- // SAFETY: DispatchSemaphore cannot be null.
45- let result = this. 0 . signal ( ) ;
46-
47- result != 0
48+ // We suppress `Drop` for the guard because that would signal the sempahore again.
49+ // The inner `DispatchRetained` is wrapped in `ManuallyDrop` so that it can be
50+ // separated from the guard and dropped normally.
51+ let mut this = ManuallyDrop :: new ( self ) ;
52+ // SAFETY: The guard is being consumed; the `ManuallyDrop` contents will not be used again.
53+ let semaphore = unsafe { ManuallyDrop :: take ( & mut this. 0 ) } ;
54+ semaphore. signal ( ) != 0
4855 }
4956}
5057
5158impl Drop for DispatchSemaphoreGuard {
5259 fn drop ( & mut self ) {
53- // SAFETY: DispatchSemaphore cannot be null.
54- self . 0 . signal ( ) ;
60+ // SAFETY: The guard is being dropped; the `ManuallyDrop` contents will not be used again.
61+ let semaphore = unsafe { ManuallyDrop :: take ( & mut self . 0 ) } ;
62+ semaphore. signal ( ) ;
63+ }
64+ }
65+
66+ #[ cfg( test) ]
67+ mod tests {
68+ #[ allow( unused_imports) ]
69+ use super :: * ;
70+
71+ #[ test]
72+ #[ cfg( feature = "objc2" ) ]
73+ #[ cfg_attr(
74+ not( target_vendor = "apple" ) ,
75+ ignore = "only Apple's libdisptch is interoperable with `objc2`"
76+ ) ]
77+ fn acquire_release ( ) {
78+ fn retain_count ( semaphore : & DispatchSemaphore ) -> usize {
79+ // SAFETY: semaphore is valid and the method signature is correct.
80+ unsafe { objc2:: msg_send![ semaphore, retainCount] }
81+ }
82+
83+ let semaphore = DispatchSemaphore :: new ( 1 ) ;
84+
85+ assert_eq ! ( retain_count( & semaphore) , 1 ) ;
86+ {
87+ let _guard = semaphore. try_acquire ( DispatchTime :: NOW ) . unwrap ( ) ;
88+ assert_eq ! ( retain_count( & semaphore) , 2 ) ;
89+ }
90+ assert_eq ! ( retain_count( & semaphore) , 1 ) ;
91+ {
92+ let guard = semaphore. try_acquire ( DispatchTime :: NOW ) . unwrap ( ) ;
93+ assert_eq ! ( retain_count( & semaphore) , 2 ) ;
94+ guard. release ( ) ;
95+ assert_eq ! ( retain_count( & semaphore) , 1 ) ;
96+ }
97+ assert_eq ! ( retain_count( & semaphore) , 1 ) ;
5598 }
5699}
0 commit comments