Skip to content

Issue on the interaction between CreationFlags and JobObject wrapper #19

@agallardol

Description

@agallardol

Hi, thanks for the library. I'm here because I was fixing an issue on another library and besides the fix it wasn't working because I tried to use CreationFlags but it isn't working. If I try to add CREATE_NO_WINDOW it's never assigned to the process itself.

I think this issue is very important for apps that really requires spawn new processes with no visible windows like MCP Dockmaster which under the hood uses rmcp the official sdk for MCPs in rust, which under the hood uses this library.

Context
On the JobObject implementation, we try to merge any previously added CreationFlag with the CREATE_SUSPENDED flag. which makes sense, even with the documented rule about use CreationFlags wrapper before the JobObject wrapper.

Issue
The spawn method temporarily takes ownership of self.wrappers using std::mem::take, which means that from there any call to self.wrappers and/or core.wrappers will return an empty array.

pub fn spawn(&mut self) -> ::std::io::Result<Box<dyn $childer>> {
let mut command = ::std::mem::replace(&mut self.command, <$command>::new(""));
let mut wrappers = ::std::mem::take(&mut self.wrappers);
let res = self.spawn_inner(&mut command, &mut wrappers);
self.command = command;
self.wrappers = wrappers;
res
}

After this mem take we call the self.spawn_inner() which uses the self instance. If at any point of this method and his sub functions we try to use core.has_wrapper and/or core.get_wrapper we will find no wrappers because we replaced temporarily the wrappers instance with an empty array.

impl TokioCommandWrapper for JobObject {
#[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
fn pre_spawn(&mut self, command: &mut Command, core: &TokioCommandWrap) -> Result<()> {
let mut flags = CREATE_SUSPENDED;
#[cfg(feature = "creation-flags")]
if let Some(CreationFlags(user_flags)) = core.get_wrap::<CreationFlags>() {
flags |= *user_flags;
}
command.creation_flags(flags.0);
Ok(())
}

How to reproduce it
Try to add the CREATE_NO_WINDOW flag following the docs instructions

    let child = TokioCommandWrap::with_new("echo", |command| {
        command.arg("hello world");
    })
    .wrap(CreationFlags(CREATE_NO_WINDOW))
    .wrap(JobObject)
    .spawn()?;

And then check the code never reachs this conditional

if let Some(CreationFlags(user_flags)) = core.get_wrap::<CreationFlags>() {
flags |= *user_flags;
}

So the flags are never merged and your process ends having just the CREATE_SUSPENDED flag

I think is very important because to be able to use this library in apps where we need to spawn new processes silently, with no visible windows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions