77// option. This file may not be copied, modified, or distributed
88// except according to those terms.
99
10+ // Most of this file won't compile without `windows-shared-memory-equality` feature on Windows since `PartialEq` won't be implemented for `IpcSharedMemory`.
11+ #![ cfg( any( not( target_os = "windows" ) , all( target_os = "windows" , feature = "windows-shared-memory-equality" ) ) ) ]
12+
1013use crate :: platform:: { self , OsIpcChannel , OsIpcReceiverSet } ;
1114use crate :: platform:: { OsIpcSharedMemory } ;
1215use std:: collections:: HashMap ;
1316use std:: sync:: Arc ;
1417use std:: time:: { Duration , Instant } ;
1518use std:: thread;
1619
20+ #[ cfg( not( any( feature = "force-inprocess" , target_os = "android" , target_os = "ios" ) ) ) ]
21+ use libc;
1722use crate :: platform:: { OsIpcSender , OsIpcOneShotServer } ;
1823#[ cfg( not( any( feature = "force-inprocess" , target_os = "windows" , target_os = "android" , target_os = "ios" ) ) ) ]
1924use libc:: { kill, SIGSTOP , SIGCONT } ;
2025#[ cfg( not( any( feature = "force-inprocess" , target_os = "windows" , target_os = "android" , target_os = "ios" ) ) ) ]
2126use crate :: test:: { fork, Wait } ;
2227
28+ // Helper to get a channel_name argument passed in; used for the
29+ // cross-process spawn server tests.
30+ #[ cfg( not( any( feature = "force-inprocess" , target_os = "android" , target_os = "ios" ) ) ) ]
31+ use crate :: test:: { get_channel_name_arg, spawn_server} ;
32+
2333#[ test]
2434fn simple ( ) {
2535 let ( tx, rx) = platform:: channel ( ) . unwrap ( ) ;
@@ -209,7 +219,8 @@ fn with_n_fds(n: usize, size: usize) {
209219
210220// These tests only apply to platforms that need fragmentation.
211221#[ cfg( all( not( feature = "force-inprocess" ) , any( target_os = "linux" ,
212- target_os = "freebsd" ) ) ) ]
222+ target_os = "freebsd" ,
223+ target_os = "windows" ) ) ) ]
213224mod fragment_tests {
214225 use crate :: platform;
215226 use super :: with_n_fds;
@@ -656,9 +667,32 @@ fn server_connect_first() {
656667 ( data, vec![ ] , vec![ ] ) ) ;
657668}
658669
670+ #[ cfg( not( any( feature = "force-inprocess" , target_os = "android" , target_os = "ios" ) ) ) ]
671+ #[ test]
672+ fn cross_process_spawn ( ) {
673+ let data: & [ u8 ] = b"1234567" ;
674+
675+ let channel_name = get_channel_name_arg ( "server" ) ;
676+ if let Some ( channel_name) = channel_name {
677+ let tx = OsIpcSender :: connect ( channel_name) . unwrap ( ) ;
678+ tx. send ( data, vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
679+
680+ unsafe { libc:: exit ( 0 ) ; }
681+ }
682+
683+ let ( server, name) = OsIpcOneShotServer :: new ( ) . unwrap ( ) ;
684+ let mut child_pid = spawn_server ( "cross_process_spawn" , & [ ( "server" , & * name) ] ) ;
685+
686+ let ( _, received_data, received_channels, received_shared_memory_regions) =
687+ server. accept ( ) . unwrap ( ) ;
688+ child_pid. wait ( ) . expect ( "failed to wait on child" ) ;
689+ assert_eq ! ( ( & received_data[ ..] , received_channels, received_shared_memory_regions) ,
690+ ( data, vec![ ] , vec![ ] ) ) ;
691+ }
692+
659693#[ cfg( not( any( feature = "force-inprocess" , target_os = "windows" , target_os = "android" , target_os = "ios" ) ) ) ]
660694#[ test]
661- fn cross_process ( ) {
695+ fn cross_process_fork ( ) {
662696 let ( server, name) = OsIpcOneShotServer :: new ( ) . unwrap ( ) ;
663697 let data: & [ u8 ] = b"1234567" ;
664698
@@ -674,9 +708,42 @@ fn cross_process() {
674708 ( data, vec![ ] , vec![ ] ) ) ;
675709}
676710
711+ #[ cfg( not( any( feature = "force-inprocess" , target_os = "android" , target_os = "ios" ) ) ) ]
712+ #[ test]
713+ fn cross_process_sender_transfer_spawn ( ) {
714+ let channel_name = get_channel_name_arg ( "server" ) ;
715+ if let Some ( channel_name) = channel_name {
716+ let super_tx = OsIpcSender :: connect ( channel_name) . unwrap ( ) ;
717+ let ( sub_tx, sub_rx) = platform:: channel ( ) . unwrap ( ) ;
718+ let data: & [ u8 ] = b"foo" ;
719+ super_tx. send ( data, vec ! [ OsIpcChannel :: Sender ( sub_tx) ] , vec ! [ ] ) . unwrap ( ) ;
720+ sub_rx. recv ( ) . unwrap ( ) ;
721+ let data: & [ u8 ] = b"bar" ;
722+ super_tx. send ( data, vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
723+
724+ unsafe { libc:: exit ( 0 ) ; }
725+ }
726+
727+ let ( server, name) = OsIpcOneShotServer :: new ( ) . unwrap ( ) ;
728+ let mut child_pid = spawn_server ( "cross_process_sender_transfer_spawn" , & [ ( "server" , & * name) ] ) ;
729+
730+ let ( super_rx, _, mut received_channels, _) = server. accept ( ) . unwrap ( ) ;
731+ assert_eq ! ( received_channels. len( ) , 1 ) ;
732+ let sub_tx = received_channels[ 0 ] . to_sender ( ) ;
733+ let data: & [ u8 ] = b"baz" ;
734+ sub_tx. send ( data, vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
735+
736+ let data: & [ u8 ] = b"bar" ;
737+ let ( received_data, received_channels, received_shared_memory_regions) =
738+ super_rx. recv ( ) . unwrap ( ) ;
739+ child_pid. wait ( ) . expect ( "failed to wait on child" ) ;
740+ assert_eq ! ( ( & received_data[ ..] , received_channels, received_shared_memory_regions) ,
741+ ( data, vec![ ] , vec![ ] ) ) ;
742+ }
743+
677744#[ cfg( not( any( feature = "force-inprocess" , target_os = "windows" , target_os = "android" , target_os = "ios" ) ) ) ]
678745#[ test]
679- fn cross_process_sender_transfer ( ) {
746+ fn cross_process_sender_transfer_fork ( ) {
680747 let ( server, name) = OsIpcOneShotServer :: new ( ) . unwrap ( ) ;
681748
682749 let child_pid = unsafe { fork ( || {
@@ -691,7 +758,7 @@ fn cross_process_sender_transfer() {
691758
692759 let ( super_rx, _, mut received_channels, _) = server. accept ( ) . unwrap ( ) ;
693760 assert_eq ! ( received_channels. len( ) , 1 ) ;
694- let sub_tx = received_channels. pop ( ) . unwrap ( ) . to_sender ( ) ;
761+ let sub_tx = received_channels[ 0 ] . to_sender ( ) ;
695762 let data: & [ u8 ] = b"baz" ;
696763 sub_tx. send ( data, vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
697764
@@ -981,3 +1048,82 @@ mod sync_test {
9811048 platform:: OsIpcSender :: test_not_sync ( ) ;
9821049 }
9831050}
1051+
1052+ // This test panics on Windows, because the other process will panic
1053+ // when it detects that it receives handles that are intended for another
1054+ // process. It's marked as ignore/known-fail on Windows for this reason.
1055+ //
1056+ // TODO -- this fails on OSX as well with a MACH_SEND_INVALID_RIGHT!
1057+ // Needs investigation. It may be a similar underlying issue, just done by
1058+ // the kernel instead of explicitly (ports in a message that's already
1059+ // buffered are intended for only one process).
1060+ #[ cfg( not( any( feature = "force-inprocess" , target_os = "android" , target_os = "ios" ) ) ) ]
1061+ #[ cfg_attr( any( target_os = "windows" , target_os = "macos" ) , ignore) ]
1062+ #[ test]
1063+ fn cross_process_two_step_transfer_spawn ( ) {
1064+ let cookie: & [ u8 ] = b"cookie" ;
1065+
1066+ let channel_name = get_channel_name_arg ( "server" ) ;
1067+ if let Some ( channel_name) = channel_name {
1068+ // connect by name to our other process
1069+ let super_tx = OsIpcSender :: connect ( channel_name) . unwrap ( ) ;
1070+
1071+ // create a channel for real communication between the two processes
1072+ let ( sub_tx, sub_rx) = platform:: channel ( ) . unwrap ( ) ;
1073+
1074+ // send the other process the tx side, so it can send us the channels
1075+ super_tx. send ( & [ ] , vec ! [ OsIpcChannel :: Sender ( sub_tx) ] , vec ! [ ] ) . unwrap ( ) ;
1076+
1077+ // get two_rx from the other process
1078+ let ( _, mut received_channels, _) = sub_rx. recv ( ) . unwrap ( ) ;
1079+ assert_eq ! ( received_channels. len( ) , 1 ) ;
1080+ let two_rx = received_channels[ 0 ] . to_receiver ( ) ;
1081+
1082+ // get one_rx from two_rx's buffer
1083+ let ( _, mut received_channels, _) = two_rx. recv ( ) . unwrap ( ) ;
1084+ assert_eq ! ( received_channels. len( ) , 1 ) ;
1085+ let one_rx = received_channels[ 0 ] . to_receiver ( ) ;
1086+
1087+ // get a cookie from one_rx
1088+ let ( data, _, _) = one_rx. recv ( ) . unwrap ( ) ;
1089+ assert_eq ! ( & data[ ..] , cookie) ;
1090+
1091+ // finally, send a cookie back
1092+ super_tx. send ( & data, vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
1093+
1094+ // terminate
1095+ unsafe { libc:: exit ( 0 ) ; }
1096+ }
1097+
1098+ // create channel 1
1099+ let ( one_tx, one_rx) = platform:: channel ( ) . unwrap ( ) ;
1100+ // put data in channel 1's pipe
1101+ one_tx. send ( cookie, vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
1102+
1103+ // create channel 2
1104+ let ( two_tx, two_rx) = platform:: channel ( ) . unwrap ( ) ;
1105+ // put channel 1's rx end in channel 2's pipe
1106+ two_tx. send ( & [ ] , vec ! [ OsIpcChannel :: Receiver ( one_rx) ] , vec ! [ ] ) . unwrap ( ) ;
1107+
1108+ // create a one-shot server, and spawn another process
1109+ let ( server, name) = OsIpcOneShotServer :: new ( ) . unwrap ( ) ;
1110+ let mut child_pid = spawn_server ( "cross_process_two_step_transfer_spawn" ,
1111+ & [ ( "server" , & * name) ] ) ;
1112+
1113+ // The other process will have sent us a transmit channel in received channels
1114+ let ( super_rx, _, mut received_channels, _) = server. accept ( ) . unwrap ( ) ;
1115+ assert_eq ! ( received_channels. len( ) , 1 ) ;
1116+ let sub_tx = received_channels[ 0 ] . to_sender ( ) ;
1117+
1118+ // Send the outer payload channel, so the server can use it to
1119+ // retrive the inner payload and the cookie
1120+ sub_tx. send ( & [ ] , vec ! [ OsIpcChannel :: Receiver ( two_rx) ] , vec ! [ ] ) . unwrap ( ) ;
1121+
1122+ // Then we wait for the cookie to make its way back to us
1123+ let ( received_data, received_channels, received_shared_memory_regions) =
1124+ super_rx. recv ( ) . unwrap ( ) ;
1125+ let child_exit_code = child_pid. wait ( ) . expect ( "failed to wait on child" ) ;
1126+ assert ! ( child_exit_code. success( ) ) ;
1127+ assert_eq ! ( ( & received_data[ ..] , received_channels, received_shared_memory_regions) ,
1128+ ( cookie, vec![ ] , vec![ ] ) ) ;
1129+ }
0 commit comments