You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I reviewed subprocess.h, System.Diagnostics.Process, as well as child_process.spawn/spawnSync initially for the api design. To start with, just the sychronous support. See async notes below.
Also, since much of the fut API is already similar to C#, I decided to keep that pattern here also. Note that I'm using internal properties here, because they seem to act much like public properties, but actually public properties would be better.
publicenum*ProcessFlags{None=0,NoWindow=0x8}publicclassProcessStartInfo{internalstring()Command;internalList<string()>()ArgumentList;internalProcessFlags*Flags;/// Start the process synchronously, block until the process exits, and return a /// ProcessSyncResult object with the exit code and output.publicProcessSyncResultStartSync();/// Start a process asynchronously in the background and return immediately.publicvoidFireAndForget();}publicclassProcessSyncResult{/// Returns the exit code if the process has already exited, /// otherwise blocks until the process exits.internalintExitCode;/// Returns all the std/err output.internalstring()Output;}
Sync / blocking process / fire and forget
This is fairly straight forward as there are blocking api's in our target langauges.
C/C++
We will need to emit the sheredom/subprocess.h header. I use the following helper function to start a process using this library:
subprocess_s util_start_subprocess(const std::vector<std::string>* command_line, int options) {
auto size = command_line->size();
constchar** command_line_array = newconstchar* [size + 1];
for (size_t i = 0; i < size; ++i) {
command_line_array[i] = command_line->at(i).c_str();
}
command_line_array[size] = NULL; // last element must be NULLstructsubprocess_s subprocess;
int result = subprocess_create(command_line_array, options, &subprocess);
delete[] command_line_array; // clean up the arrayif (result != 0) {
throwstd::runtime_error("Unable to start Update process.");
}
return subprocess;
}
The first argument is the command/path to exe, and the following arguments are the program arguments.
The subprocess_s type is our Process type. For options, we should map ProcessFlags to subprocess_option_* but also should always add the subprocess_option_combined_stdout_stderr option.
The FireAndForget function should just call util_start_subprocess and discard the result.
For the StartBlocking function:
public ProcessSyncResult StartSync()
{
int options = subprocess_option_combined_stdout_stderr;
if ((Flags & ProcessFlags.NoWindow) == ProcessFlags.NoWindow)
options |= subprocess_option_no_window;
// combine args and command into one arrayauto command = ArgumentList;
command.insert(command.begin(), Command);
subprocess_s process = util_start_subprocess(command, options);
FILE* p_stdout = subprocess_stdout(&process);
int exitCode = 0;
subprocess_join(process, &exitCode);
std::filebuf buf = std::basic_filebuf<char>(p_stdout);
std::istream is(&buf);
std::stringstream buffer;
buffer << is.rdbuf();
std::string output = buffer.str();
ProcessSyncResult result;
result.Output = output;
result.ExitCode = exitcode;
return result;
}
C#
Very straight forward, because we can just map most straight across.
publicProcessSyncResultStartSync(){varpsi=newProcessStartInfo(){CreateNoWindow=(Flags&ProcessFlags.NoWindow)==ProcessFlags.NoWindow,FileName=Command,// mapped from our CommandRedirectStandardError=true,// always trueRedirectStandardInput=true,// always trueUseShellExecute=false,// always false};foreach(vararginArgumentList){psi.ArgumentList.Add(arg);}StringBuilderoutput=newStringBuilder();// we use async events here instead of StandardOutput.ReadToEnd()// because it means that the buffer can be processed and it wont // fill up and block the process. Also, it means stdout and stderr// get interleaved properly.varprocess=newProcess();process.StartInfo=psi;process.ErrorDataReceived+=(sender,e)=>{if(e.Data!=null){output.AppendLine(e.Data);}};process.OutputDataReceived+=(sender,e)=>{if(e.Data!=null){output.AppendLine(e.Data);}};process.Start();process.BeginErrorReadLine();process.BeginOutputReadLine();process.WaitForExit();ProcessSyncResultresult;result.Output=output.ToString();result.ExitCode=process.ExitCode;returnresult;}
std.process.pipeProcess can help us to merge the streams and read all output in one go.
importstd.stdio;
importstd.array;
importstd.process;
public ProcessSyncResult StartSync()
{
// combine command and argumentlist into CommandArgsauto pipes = pipeProcess(CommandArgs, Redirect.stdout | Redirect.stderr);
// Read the process output (stdout and stderr)string output = cast(string) pipes.readAllUTF8();
// Wait for the process to complete and get the exit codeauto status = wait(pipes.pid);
int exitCode = status.status;
ProcessSyncResult result;
result.Output = output;
result.ExitCode = exitCode;
return result;
}
OpenCL
I don't think we can / should support this platform. Using Process should result in a compiler error.
Java
We can use the ProcessBuilder which takes a List as a constructor argument
publicProcessSyncResultStartSync()
{
// merge Command and ArgumentList into CommandArgsProcessBuilderbuilder = newProcessBuilder(CommandArgs);
builder.redirectErrorStream(true); // both out and err go to getInputStreamProcessprocess = builder.start();
StringBuilderprocessOutput = newStringBuilder();
intexitCode = -1;
try (BufferedReaderprocessOutputReader = newBufferedReader(newInputStreamReader(process.getInputStream()));)
{
StringreadLine;
while ((readLine = processOutputReader.readLine()) != null)
{
processOutput.append(readLine + System.lineSeparator());
}
exitCode = process.waitFor();
}
Stringoutput = processOutput.toString().trim();
ProcessSyncResultresult;
result.Output = output;
result.ExitCode = exitCode;
returnresult;
}
Python
importsubprocesspublicProcessSyncResultStartSync()
{
# Combine the executable and the arguments into a single commandcommand= [Command] +ArgumentList# Run the command, capture stdout, stderr, and the exit codeprocess=subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
stdout, _=process.communicate() # This will block until the process completes# `stdout` contains the combined output of stdout and stderrProcessSyncResultresult;
result.Output=stdout;
result.ExitCode=process.returncode;
returnresult;
}
There are some tricky bits here, because as far as I know there are no support for delegates or async callbacks of any kind in fut.
Perhaps we should spin out delegates/callbacks as a separate proposal? Since fut also doesn't support exception handling, we could spin out something akin to the javascript promise api and then map that to language specific features. Many languages have native async/await support, including JS/TS, python, C#, but not every language can map to async/await. Java has CompletableFuture, C# has TaskCompletionSource, JS/TS has promise, and C/C++ has callbacks, so I think that a promise/callback approach will be more compatible overall.
C/C++
public bool HasExited() {
return subprocess_alive(process) != 0;
}
C#
Js/Ts
D
OpenCL
I don't think we can / should support this platform. Using Process should result in a compiler error.
Java
Python
Swift
The text was updated successfully, but these errors were encountered:
API Design
I reviewed subprocess.h, System.Diagnostics.Process, as well as child_process.spawn/spawnSync initially for the api design. To start with, just the sychronous support. See async notes below.
Also, since much of the fut API is already similar to C#, I decided to keep that pattern here also. Note that I'm using internal properties here, because they seem to act much like public properties, but actually public properties would be better.
Sync / blocking process / fire and forget
This is fairly straight forward as there are blocking api's in our target langauges.
C/C++
We will need to emit the sheredom/subprocess.h header. I use the following helper function to start a process using this library:
The first argument is the command/path to exe, and the following arguments are the program arguments.
The subprocess_s type is our Process type. For options, we should map ProcessFlags to
subprocess_option_*
but also should always add thesubprocess_option_combined_stdout_stderr
option.The FireAndForget function should just call util_start_subprocess and discard the result.
For the StartBlocking function:
C#
Very straight forward, because we can just map most straight across.
Js/Ts
D
std.process.pipeProcess can help us to merge the streams and read all output in one go.
OpenCL
I don't think we can / should support this platform. Using Process should result in a compiler error.
Java
We can use the ProcessBuilder which takes a List as a constructor argument
Python
Swift
Async / readline
There are some tricky bits here, because as far as I know there are no support for delegates or async callbacks of any kind in fut.
Perhaps we should spin out delegates/callbacks as a separate proposal? Since fut also doesn't support exception handling, we could spin out something akin to the javascript promise api and then map that to language specific features. Many languages have native async/await support, including JS/TS, python, C#, but not every language can map to async/await. Java has CompletableFuture, C# has TaskCompletionSource, JS/TS has promise, and C/C++ has callbacks, so I think that a promise/callback approach will be more compatible overall.
C/C++
public bool HasExited() {
return subprocess_alive(process) != 0;
}
C#
Js/Ts
D
OpenCL
I don't think we can / should support this platform. Using Process should result in a compiler error.
Java
Python
Swift
The text was updated successfully, but these errors were encountered: