@@ -24,16 +24,35 @@ cfg_if::cfg_if! {
2424 }
2525}
2626
27+ cfg_if:: cfg_if! {
28+ if #[ cfg( all( windows, feature = "iocp" ) ) ] {
29+ use dashmap:: DashMap ;
30+ use std:: ffi:: { c_longlong, c_uint} ;
31+ use windows_sys:: core:: { PCSTR , PSTR } ;
32+ use windows_sys:: Win32 :: Networking :: WinSock :: {
33+ setsockopt, LPWSAOVERLAPPED_COMPLETION_ROUTINE , SEND_RECV_FLAGS , SOCKADDR , SOCKET , SOL_SOCKET ,
34+ SO_UPDATE_ACCEPT_CONTEXT , WSABUF ,
35+ } ;
36+ use windows_sys:: Win32 :: System :: IO :: OVERLAPPED ;
37+ }
38+ }
39+
2740#[ repr( C ) ]
2841#[ derive( Debug ) ]
2942pub ( crate ) struct EventLoop < ' e > {
3043 stop : Arc < ( Mutex < bool > , Condvar ) > ,
3144 shared_stop : Arc < ( Mutex < AtomicUsize > , Condvar ) > ,
3245 cpu : usize ,
33- #[ cfg( all( target_os = "linux" , feature = "io_uring" ) ) ]
46+ #[ cfg( any(
47+ all( target_os = "linux" , feature = "io_uring" ) ,
48+ all( windows, feature = "iocp" )
49+ ) ) ]
3450 operator : crate :: net:: operator:: Operator < ' e > ,
3551 #[ allow( clippy:: type_complexity) ]
36- #[ cfg( all( target_os = "linux" , feature = "io_uring" ) ) ]
52+ #[ cfg( any(
53+ all( target_os = "linux" , feature = "io_uring" ) ,
54+ all( windows, feature = "iocp" )
55+ ) ) ]
3756 syscall_wait_table : DashMap < usize , Arc < ( Mutex < Option < c_longlong > > , Condvar ) > > ,
3857 selector : Poller ,
3958 pool : CoroutinePool < ' e > ,
@@ -87,9 +106,15 @@ impl<'e> EventLoop<'e> {
87106 stop : Arc :: new ( ( Mutex :: new ( false ) , Condvar :: new ( ) ) ) ,
88107 shared_stop,
89108 cpu,
90- #[ cfg( all( target_os = "linux" , feature = "io_uring" ) ) ]
109+ #[ cfg( any(
110+ all( target_os = "linux" , feature = "io_uring" ) ,
111+ all( windows, feature = "iocp" )
112+ ) ) ]
91113 operator : crate :: net:: operator:: Operator :: new ( cpu) ?,
92- #[ cfg( all( target_os = "linux" , feature = "io_uring" ) ) ]
114+ #[ cfg( any(
115+ all( target_os = "linux" , feature = "io_uring" ) ,
116+ all( windows, feature = "iocp" )
117+ ) ) ]
93118 syscall_wait_table : DashMap :: new ( ) ,
94119 selector : Poller :: new ( ) ?,
95120 pool : CoroutinePool :: new ( name, stack_size, min_size, max_size, keep_alive_time) ,
@@ -222,6 +247,8 @@ impl<'e> EventLoop<'e> {
222247 cfg_if:: cfg_if! {
223248 if #[ cfg( all( target_os = "linux" , feature = "io_uring" ) ) ] {
224249 left_time = self . adapt_io_uring( left_time) ?;
250+ } else if #[ cfg( all( windows, feature = "iocp" ) ) ] {
251+ left_time = self . adapt_iocp( left_time) ?;
225252 }
226253 }
227254
@@ -267,6 +294,52 @@ impl<'e> EventLoop<'e> {
267294 Ok ( left_time)
268295 }
269296
297+ #[ cfg( all( windows, feature = "iocp" ) ) ]
298+ fn adapt_iocp ( & self , mut left_time : Option < Duration > ) -> std:: io:: Result < Option < Duration > > {
299+ // use IOCP
300+ let ( count, mut cq, left) = self . operator . select ( left_time, 0 ) ?;
301+ if count > 0 {
302+ for cqe in & mut cq {
303+ let token = cqe. token ;
304+ let bytes_transferred = cqe. bytes_transferred ;
305+ // resolve completed read/write tasks
306+ // todo refactor IOCP impl
307+ let result = match cqe. syscall_name {
308+ SyscallName :: accept => unsafe {
309+ if setsockopt (
310+ cqe. socket ,
311+ SOL_SOCKET ,
312+ SO_UPDATE_ACCEPT_CONTEXT ,
313+ std:: ptr:: from_ref ( & cqe. from_fd ) . cast ( ) ,
314+ c_int:: try_from ( size_of :: < SOCKET > ( ) ) . expect ( "overflow" ) ,
315+ ) == 0
316+ {
317+ cqe. socket . try_into ( ) . expect ( "result overflow" )
318+ } else {
319+ -c_longlong:: from ( windows_sys:: Win32 :: Foundation :: GetLastError ( ) )
320+ }
321+ } ,
322+ SyscallName :: recv
323+ | SyscallName :: WSARecv
324+ | SyscallName :: send
325+ | SyscallName :: WSASend => bytes_transferred. into ( ) ,
326+ _ => panic ! ( "unsupported" ) ,
327+ } ;
328+ if let Some ( ( _, pair) ) = self . syscall_wait_table . remove ( & token) {
329+ let ( lock, cvar) = & * pair;
330+ let mut pending = lock. lock ( ) . expect ( "lock failed" ) ;
331+ * pending = Some ( result) ;
332+ cvar. notify_one ( ) ;
333+ }
334+ unsafe { self . resume ( token) } ;
335+ }
336+ }
337+ if left != left_time {
338+ left_time = Some ( left. unwrap_or ( Duration :: ZERO ) ) ;
339+ }
340+ Ok ( left_time)
341+ }
342+
270343 unsafe fn resume ( & self , token : usize ) {
271344 if COROUTINE_TOKENS . remove ( & token) . is_none ( ) {
272345 return ;
@@ -446,6 +519,34 @@ impl_io_uring!(mkdirat(dirfd: c_int, pathname: *const c_char, mode: mode_t) -> c
446519impl_io_uring ! ( renameat( olddirfd: c_int, oldpath: * const c_char, newdirfd: c_int, newpath: * const c_char) -> c_int) ;
447520impl_io_uring ! ( renameat2( olddirfd: c_int, oldpath: * const c_char, newdirfd: c_int, newpath: * const c_char, flags: c_uint) -> c_int) ;
448521
522+ macro_rules! impl_iocp {
523+ ( $syscall: ident( $( $arg: ident : $arg_type: ty) ,* ) -> $result: ty ) => {
524+ #[ cfg( all( windows, feature = "iocp" ) ) ]
525+ impl EventLoop <' _> {
526+ #[ allow( non_snake_case, clippy:: too_many_arguments) ]
527+ pub ( super ) fn $syscall(
528+ & self ,
529+ $( $arg: $arg_type) ,*
530+ ) -> std:: io:: Result <Arc <( Mutex <Option <c_longlong>>, Condvar ) >> {
531+ let token = EventLoop :: token( SyscallName :: $syscall) ;
532+ self . operator. $syscall( token, $( $arg, ) * ) ?;
533+ let arc = Arc :: new( ( Mutex :: new( None ) , Condvar :: new( ) ) ) ;
534+ assert!(
535+ self . syscall_wait_table. insert( token, arc. clone( ) ) . is_none( ) ,
536+ "The previous token was not retrieved in a timely manner"
537+ ) ;
538+ Ok ( arc)
539+ }
540+ }
541+ }
542+ }
543+
544+ impl_iocp ! ( accept( fd: SOCKET , addr: * mut SOCKADDR , len: * mut c_int) -> c_int) ;
545+ impl_iocp ! ( recv( fd: SOCKET , buf: PSTR , len: c_int, flags: SEND_RECV_FLAGS ) -> c_int) ;
546+ impl_iocp ! ( WSARecv ( fd: SOCKET , buf: * const WSABUF , dwbuffercount: c_uint, lpnumberofbytesrecvd: * mut c_uint, lpflags : * mut c_uint, lpoverlapped: * mut OVERLAPPED , lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE ) -> c_int) ;
547+ impl_iocp ! ( send( fd: SOCKET , buf: PCSTR , len: c_int, flags: SEND_RECV_FLAGS ) -> c_int) ;
548+ impl_iocp ! ( WSASend ( fd: SOCKET , buf: * const WSABUF , dwbuffercount: c_uint, lpnumberofbytesrecvd: * mut c_uint, dwflags : c_uint, lpoverlapped: * mut OVERLAPPED , lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE ) -> c_int) ;
549+
449550#[ cfg( all( test, not( all( unix, feature = "preemptive" ) ) ) ) ]
450551mod tests {
451552 use crate :: net:: event_loop:: EventLoop ;
0 commit comments