@@ -249,7 +249,7 @@ use std::future::Future;
249249use std:: io;
250250use std:: path:: Path ;
251251use std:: pin:: Pin ;
252- use std:: process:: { Command as StdCommand , ExitStatus , Output , Stdio } ;
252+ use std:: process:: { Child as StdChild , Command as StdCommand , ExitStatus , Output , Stdio } ;
253253use std:: task:: { ready, Context , Poll } ;
254254
255255#[ cfg( unix) ]
@@ -860,8 +860,67 @@ impl Command {
860860 /// On Unix platforms this method will fail with `std::io::ErrorKind::WouldBlock`
861861 /// if the system process limit is reached (which includes other applications
862862 /// running on the system).
863+ #[ inline]
863864 pub fn spawn ( & mut self ) -> io:: Result < Child > {
864- imp:: spawn_child ( & mut self . std ) . map ( |spawned_child| Child {
865+ self . spawn_with ( StdCommand :: spawn)
866+ }
867+
868+ /// Executes the command as a child process with a custom spawning function,
869+ /// returning a handle to it.
870+ ///
871+ /// This is identical to [`Self::spawn`] in every aspect except the spawn:
872+ /// here it customizable through the `with` parameter instead of defaulting
873+ /// to the usual spawn. In fact, [`Self::spawn`] is just [`Self::spawn_with`]
874+ /// with [`StdCommand::spawn`].
875+ ///
876+ /// This is useful mostly under Windows for now, since the platform exposes
877+ /// special APIs to configure child processes when spawning them with various
878+ /// attributes that customize the exact behavior of the spawn operation.
879+ ///
880+ /// # Examples
881+ ///
882+ /// Basic usage:
883+ ///
884+ /// ```no_run
885+ /// # async fn test() { // allow using await
886+ /// let output = tokio::process::Command::new("ls")
887+ /// .spawn_with(std::process::Command::spawn)
888+ /// .output()
889+ /// .await
890+ /// .unwrap();
891+ /// # }
892+ /// ```
893+ ///
894+ /// Actually customizing the spawn under Windows:
895+ ///
896+ /// ```ignore
897+ /// # #[cfg(windows)] // Windows-only nightly APIs are used here.
898+ /// # async fn test() { // Allow using await.
899+ /// use std::os::windows::io::AsRawHandle;
900+ /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
901+ /// use tokio::process::Command;
902+ ///
903+ /// let parent = Command::new("cmd").spawn().unwrap();
904+ /// let parent_process_handle = parent.as_raw_handle();
905+ ///
906+ /// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
907+ /// let mut attribute_list = ProcThreadAttributeList::build()
908+ /// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
909+ /// .finish()
910+ /// .unwrap();
911+ ///
912+ /// let output = Command::new("ls")
913+ /// .spawn_with(|cmd| cmd.spawn_with_attributes(&attribute_list))
914+ /// .output()
915+ /// .await
916+ /// .unwrap();
917+ /// # }
918+ /// ```
919+ pub fn spawn_with (
920+ & mut self ,
921+ with : impl Fn ( & mut StdCommand ) -> io:: Result < StdChild > ,
922+ ) -> io:: Result < Child > {
923+ imp:: spawn_child_with ( & mut self . std , with) . map ( |spawned_child| Child {
865924 child : FusedChild :: Child ( ChildDropGuard {
866925 inner : spawned_child. child ,
867926 kill_on_drop : self . kill_on_drop ,
0 commit comments