@@ -12,7 +12,6 @@ pub(crate) use event::Event;
1212pub ( crate ) use pipe:: MessagePipeServer ;
1313pub ( crate ) use semaphore:: Semaphore ;
1414
15- use smallvec:: SmallVec ;
1615use windows:: Win32 :: Foundation :: {
1716 ERROR_IO_PENDING , HANDLE , WAIT_ABANDONED_0 , WAIT_EVENT , WAIT_FAILED , WAIT_OBJECT_0 , WAIT_TIMEOUT ,
1817} ;
@@ -21,17 +20,19 @@ use windows::Win32::System::Threading::{WaitForMultipleObjects, INFINITE};
2120/// Thin wrapper around borrowed `windows` crate `HANDLE` reference.
2221/// This is used to ensure handle lifetime when passing it to FFI functions
2322/// (see `wait_any_with_timeout` for example).
23+ #[ repr( transparent) ]
2424pub ( crate ) struct BorrowedHandle < ' a > ( & ' a HANDLE ) ;
2525
2626/// Safe wrapper around `WaitForMultipleObjects`.
27- pub ( crate ) fn wait_any_with_timeout < ' a , T > ( handles : T , timeout : u32 ) -> Result < usize , WindowsError >
28- where
29- T : IntoIterator < Item = BorrowedHandle < ' a > > ,
30- {
31- let handles: SmallVec < [ HANDLE ; 8 ] > = handles. into_iter ( ) . map ( |h| * h. 0 ) . collect ( ) ;
27+ pub ( crate ) fn wait_any_with_timeout ( handles : & [ BorrowedHandle < ' _ > ] , timeout : u32 ) -> Result < usize , WindowsError > {
28+ let handles = cast_handles ( handles) ;
3229
33- // SAFETY: FFI call with no outstanding preconditions.
34- let result = unsafe { WaitForMultipleObjects ( & handles, false , timeout) } ;
30+ // SAFETY:
31+ // - BorrowedHandle alongside with rust type system ensures that the HANDLEs are valid for
32+ // the duration of the call.
33+ // - All handles in this module have SYNCHRONIZE access rights.
34+ // - cast_handles ensures no handle duplicates.
35+ let result = unsafe { WaitForMultipleObjects ( handles, false , timeout) } ;
3536
3637 match result {
3738 WAIT_FAILED => Err ( WindowsError :: WaitForMultipleObjectsFailed (
@@ -47,17 +48,45 @@ where
4748}
4849
4950/// Safe `WaitForMultipleObjects` wrapper with infinite timeout.
50- pub ( crate ) fn wait_any < ' a , T > ( handles : T ) -> Result < usize , WindowsError >
51- where
52- T : IntoIterator < Item = BorrowedHandle < ' a > > ,
53- {
51+ pub ( crate ) fn wait_any ( handles : & [ BorrowedHandle < ' _ > ] ) -> Result < usize , WindowsError > {
5452 // Standard generic syntax is used instead if `impl` because of the following lint:
5553 // > warning: lifetime parameter `'a` only used once
5654 //
5755 // Fixing this lint (use of '_ lifetime) produces compiler error.
5856 wait_any_with_timeout ( handles, INFINITE )
5957}
6058
59+ fn cast_handles < ' a > ( handles : & ' a [ BorrowedHandle < ' a > ] ) -> & ' a [ HANDLE ] {
60+ // Very basic sanity checks to ensure that the handles are valid
61+ // and there are no duplicates.
62+ // This is only done in debug builds to avoid performance overhead in release builds, while
63+ // still catching undefined behavior early in development.
64+ #[ cfg( debug_assertions) ]
65+ {
66+ // Ensure that there are no duplicate handles without hash.
67+ for ( i, handle) in handles. iter ( ) . enumerate ( ) {
68+ for other_handle in & handles[ i + 1 ..] {
69+ if handle. 0 == other_handle. 0 {
70+ panic ! ( "Duplicate handle found in wait_any_with_timeout" ) ;
71+ }
72+ }
73+ }
74+ }
75+
76+ for handle in handles {
77+ // Ensure that the handle is valid.
78+ if handle. 0 . is_invalid ( ) {
79+ panic ! ( "Invalid handle in wait_any_with_timeout" ) ;
80+ }
81+ }
82+
83+ // SAFETY:
84+ // - BorrowedHandle is #[repr(transparent)] over *const c_void, and so is HANDLE,
85+ // so the layout is the same.
86+ // - We ensure the lifetime is preserved.
87+ unsafe { core:: slice:: from_raw_parts ( handles. as_ptr ( ) as * const HANDLE , handles. len ( ) ) }
88+ }
89+
6190/// Maps ERROR_IO_PENDING to Ok(()) and returns other errors as is.
6291fn ensure_overlapped_io_result ( result : windows:: core:: Result < ( ) > ) -> Result < windows:: core:: Result < ( ) > , WindowsError > {
6392 if let Err ( error) = & result {
0 commit comments