Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change setup_stdio default method to support any IO #40780

Merged
merged 1 commit into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base/cmd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ rawhandle(x::OS_HANDLE) = x
if OS_HANDLE !== RawFD
rawhandle(x::RawFD) = Libc._get_osfhandle(x)
end
setup_stdio(stdio::Union{DevNull,OS_HANDLE,RawFD}, ::Bool) = (stdio, false)

const Redirectable = Union{IO, FileRedirect, RawFD, OS_HANDLE}
const StdIOSet = NTuple{3, Redirectable}
Expand Down
3 changes: 2 additions & 1 deletion base/filesystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import .Base:
IOError, _UVError, _sizeof_uv_fs, check_open, close, eof, eventloop, fd, isopen,
bytesavailable, position, read, read!, readavailable, seek, seekend, show,
skip, stat, unsafe_read, unsafe_write, write, transcode, uv_error,
rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize
setup_stdio, rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize

import .Base.RefValue

Expand Down Expand Up @@ -76,6 +76,7 @@ if OS_HANDLE !== RawFD
end

rawhandle(file::File) = file.handle
setup_stdio(file::File, ::Bool) = (file, false)

# Filesystem.open, not Base.open
function open(path::AbstractString, flags::Integer, mode::Integer=0)
Expand Down
66 changes: 31 additions & 35 deletions base/process.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,28 @@ const SpawnIOs = Vector{Any} # convenience name for readability
# handle marshalling of `Cmd` arguments from Julia to C
@noinline function _spawn_primitive(file, cmd::Cmd, stdio::SpawnIOs)
loop = eventloop()
iohandles = Tuple{Cint, UInt}[ # assuming little-endian layout
let h = rawhandle(io)
h === C_NULL ? (0x00, UInt(0)) :
h isa OS_HANDLE ? (0x02, UInt(cconvert(@static(Sys.iswindows() ? Ptr{Cvoid} : Cint), h))) :
h isa Ptr{Cvoid} ? (0x04, UInt(h)) :
error("invalid spawn handle $h from $io")
end
for io in stdio]
handle = Libc.malloc(_sizeof_uv_process)
disassociate_julia_struct(handle) # ensure that data field is set to C_NULL
err = ccall(:jl_spawn, Int32,
(Cstring, Ptr{Cstring}, Ptr{Cvoid}, Ptr{Cvoid},
Ptr{Tuple{Cint, UInt}}, Int,
UInt32, Ptr{Cstring}, Cstring, Ptr{Cvoid}),
file, cmd.exec, loop, handle,
iohandles, length(iohandles),
cmd.flags,
cmd.env === nothing ? C_NULL : cmd.env,
isempty(cmd.dir) ? C_NULL : cmd.dir,
@cfunction(uv_return_spawn, Cvoid, (Ptr{Cvoid}, Int64, Int32)))
GC.@preserve stdio begin
iohandles = Tuple{Cint, UInt}[ # assuming little-endian layout
let h = rawhandle(io)
h === C_NULL ? (0x00, UInt(0)) :
h isa OS_HANDLE ? (0x02, UInt(cconvert(@static(Sys.iswindows() ? Ptr{Cvoid} : Cint), h))) :
h isa Ptr{Cvoid} ? (0x04, UInt(h)) :
error("invalid spawn handle $h from $io")
end
for io in stdio]
handle = Libc.malloc(_sizeof_uv_process)
disassociate_julia_struct(handle) # ensure that data field is set to C_NULL
err = ccall(:jl_spawn, Int32,
(Cstring, Ptr{Cstring}, Ptr{Cvoid}, Ptr{Cvoid},
Ptr{Tuple{Cint, UInt}}, Int,
UInt32, Ptr{Cstring}, Cstring, Ptr{Cvoid}),
file, cmd.exec, loop, handle,
iohandles, length(iohandles),
cmd.flags,
cmd.env === nothing ? C_NULL : cmd.env,
isempty(cmd.dir) ? C_NULL : cmd.dir,
@cfunction(uv_return_spawn, Cvoid, (Ptr{Cvoid}, Int64, Int32)))
end
if err != 0
ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), handle) # will call free on handle eventually
throw(_UVError("could not spawn " * repr(cmd), err))
Expand Down Expand Up @@ -209,10 +211,10 @@ function setup_stdio(stdio::PipeEndpoint, child_readable::Bool)
rd, wr = link_pipe(!child_readable, child_readable)
try
open_pipe!(stdio, child_readable ? wr : rd)
catch ex
catch
close_pipe_sync(rd)
close_pipe_sync(wr)
rethrow(ex)
rethrow()
end
child = child_readable ? rd : wr
return (child, true)
Expand Down Expand Up @@ -251,18 +253,18 @@ function setup_stdio(stdio::FileRedirect, child_readable::Bool)
return (io, true)
end

# incrementally move data between an IOBuffer and a system Pipe
# incrementally move data between an arbitrary IO and a system Pipe
# TODO: probably more efficient (when valid) to use `stdio` directly as the
# PipeEndpoint buffer field in some cases
function setup_stdio(stdio::Union{IOBuffer, BufferStream}, child_readable::Bool)
function setup_stdio(stdio::IO, child_readable::Bool)
parent = PipeEndpoint()
rd, wr = link_pipe(!child_readable, child_readable)
try
open_pipe!(parent, child_readable ? wr : rd)
catch ex
catch
close_pipe_sync(rd)
close_pipe_sync(wr)
rethrow(ex)
rethrow()
end
child = child_readable ? rd : wr
try
Expand All @@ -271,24 +273,18 @@ function setup_stdio(stdio::Union{IOBuffer, BufferStream}, child_readable::Bool)
@async try
write(in, out)
catch ex
@warn "Process error" exception=(ex, catch_backtrace())
@warn "Process I/O error" exception=(ex, catch_backtrace())
finally
close(parent)
end
end
catch ex
catch
close_pipe_sync(child)
rethrow(ex)
rethrow()
end
return (child, true)
end

function setup_stdio(io, child_readable::Bool)
# if there is no specialization,
# assume that rawhandle is defined for it
return (io, false)
end

close_stdio(stdio::OS_HANDLE) = close_pipe_sync(stdio)
close_stdio(stdio) = close(stdio)

Expand Down
1 change: 1 addition & 0 deletions base/stream.jl
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ end
lock(s::LibuvStream) = lock(s.lock)
unlock(s::LibuvStream) = unlock(s.lock)

setup_stdio(stream::LibuvStream) = (stream, false)
rawhandle(stream::LibuvStream) = stream.handle
unsafe_convert(::Type{Ptr{Cvoid}}, s::Union{LibuvStream, LibuvServer}) = s.handle

Expand Down
8 changes: 8 additions & 0 deletions test/spawn.jl
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,15 @@ let text = "input-test-text"
@test read(proc, String) == string(length(text), '\n')
@test success(proc)
@test String(take!(b)) == text

out = PipeBuffer()
proc = run(catcmd, IOBuffer(SubString(text)), out)
@test success(proc)
@test proc.out === proc.err === proc.in === devnull
@test String(take!(out)) == text
end


@test repr(Base.CmdRedirect(``, devnull, 0, false)) == "pipeline(``, stdin>Base.DevNull())"
@test repr(Base.CmdRedirect(``, devnull, 1, true)) == "pipeline(``, stdout<Base.DevNull())"
@test repr(Base.CmdRedirect(``, devnull, 11, true)) == "pipeline(``, 11<Base.DevNull())"
Expand Down