Skip to content

Commit 3aada59

Browse files
authored
Reland "change setup_stdio default method to support any IO (#40780)" (#40971) (#40973)
This reverts commit 7edd190, and fixes a method type signature error, which caused TTY to get shutdown.
1 parent 4598966 commit 3aada59

File tree

5 files changed

+44
-37
lines changed

5 files changed

+44
-37
lines changed

base/cmd.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ rawhandle(x::OS_HANDLE) = x
167167
if OS_HANDLE !== RawFD
168168
rawhandle(x::RawFD) = Libc._get_osfhandle(x)
169169
end
170+
setup_stdio(stdio::Union{DevNull,OS_HANDLE,RawFD}, ::Bool) = (stdio, false)
170171

171172
const Redirectable = Union{IO, FileRedirect, RawFD, OS_HANDLE}
172173
const StdIOSet = NTuple{3, Redirectable}

base/filesystem.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import .Base:
5858
IOError, _UVError, _sizeof_uv_fs, check_open, close, eof, eventloop, fd, isopen,
5959
bytesavailable, position, read, read!, readavailable, seek, seekend, show,
6060
skip, stat, unsafe_read, unsafe_write, write, transcode, uv_error,
61-
rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize
61+
setup_stdio, rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize
6262

6363
import .Base.RefValue
6464

@@ -92,6 +92,7 @@ if OS_HANDLE !== RawFD
9292
end
9393

9494
rawhandle(file::File) = file.handle
95+
setup_stdio(file::File, ::Bool) = (file, false)
9596

9697
# Filesystem.open, not Base.open
9798
function open(path::AbstractString, flags::Integer, mode::Integer=0)

base/process.jl

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -74,27 +74,29 @@ const SpawnIOs = Vector{Any} # convenience name for readability
7474
# handle marshalling of `Cmd` arguments from Julia to C
7575
@noinline function _spawn_primitive(file, cmd::Cmd, stdio::SpawnIOs)
7676
loop = eventloop()
77-
iohandles = Tuple{Cint, UInt}[ # assuming little-endian layout
78-
let h = rawhandle(io)
79-
h === C_NULL ? (0x00, UInt(0)) :
80-
h isa OS_HANDLE ? (0x02, UInt(cconvert(@static(Sys.iswindows() ? Ptr{Cvoid} : Cint), h))) :
81-
h isa Ptr{Cvoid} ? (0x04, UInt(h)) :
82-
error("invalid spawn handle $h from $io")
83-
end
84-
for io in stdio]
85-
handle = Libc.malloc(_sizeof_uv_process)
86-
disassociate_julia_struct(handle) # ensure that data field is set to C_NULL
87-
(; exec, flags, env, dir) = cmd
88-
err = ccall(:jl_spawn, Int32,
89-
(Cstring, Ptr{Cstring}, Ptr{Cvoid}, Ptr{Cvoid},
90-
Ptr{Tuple{Cint, UInt}}, Int,
91-
UInt32, Ptr{Cstring}, Cstring, Ptr{Cvoid}),
92-
file, exec, loop, handle,
93-
iohandles, length(iohandles),
94-
flags,
95-
env === nothing ? C_NULL : env,
96-
isempty(dir) ? C_NULL : dir,
97-
@cfunction(uv_return_spawn, Cvoid, (Ptr{Cvoid}, Int64, Int32)))
77+
GC.@preserve stdio begin
78+
iohandles = Tuple{Cint, UInt}[ # assuming little-endian layout
79+
let h = rawhandle(io)
80+
h === C_NULL ? (0x00, UInt(0)) :
81+
h isa OS_HANDLE ? (0x02, UInt(cconvert(@static(Sys.iswindows() ? Ptr{Cvoid} : Cint), h))) :
82+
h isa Ptr{Cvoid} ? (0x04, UInt(h)) :
83+
error("invalid spawn handle $h from $io")
84+
end
85+
for io in stdio]
86+
handle = Libc.malloc(_sizeof_uv_process)
87+
disassociate_julia_struct(handle) # ensure that data field is set to C_NULL
88+
(; exec, flags, env, dir) = cmd
89+
err = ccall(:jl_spawn, Int32,
90+
(Cstring, Ptr{Cstring}, Ptr{Cvoid}, Ptr{Cvoid},
91+
Ptr{Tuple{Cint, UInt}}, Int,
92+
UInt32, Ptr{Cstring}, Cstring, Ptr{Cvoid}),
93+
file, exec, loop, handle,
94+
iohandles, length(iohandles),
95+
flags,
96+
env === nothing ? C_NULL : env,
97+
isempty(dir) ? C_NULL : dir,
98+
@cfunction(uv_return_spawn, Cvoid, (Ptr{Cvoid}, Int64, Int32)))
99+
end
98100
if err != 0
99101
ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), handle) # will call free on handle eventually
100102
throw(_UVError("could not spawn " * repr(cmd), err))
@@ -210,10 +212,10 @@ function setup_stdio(stdio::PipeEndpoint, child_readable::Bool)
210212
rd, wr = link_pipe(!child_readable, child_readable)
211213
try
212214
open_pipe!(stdio, child_readable ? wr : rd)
213-
catch ex
215+
catch
214216
close_pipe_sync(rd)
215217
close_pipe_sync(wr)
216-
rethrow(ex)
218+
rethrow()
217219
end
218220
child = child_readable ? rd : wr
219221
return (child, true)
@@ -252,18 +254,19 @@ function setup_stdio(stdio::FileRedirect, child_readable::Bool)
252254
return (io, true)
253255
end
254256

255-
# incrementally move data between an IOBuffer and a system Pipe
257+
# incrementally move data between an arbitrary IO and a system Pipe,
258+
# including copying the EOF (shutdown) when finished
256259
# TODO: probably more efficient (when valid) to use `stdio` directly as the
257260
# PipeEndpoint buffer field in some cases
258-
function setup_stdio(stdio::Union{IOBuffer, BufferStream}, child_readable::Bool)
261+
function setup_stdio(stdio::IO, child_readable::Bool)
259262
parent = PipeEndpoint()
260263
rd, wr = link_pipe(!child_readable, child_readable)
261264
try
262265
open_pipe!(parent, child_readable ? wr : rd)
263-
catch ex
266+
catch
264267
close_pipe_sync(rd)
265268
close_pipe_sync(wr)
266-
rethrow(ex)
269+
rethrow()
267270
end
268271
child = child_readable ? rd : wr
269272
try
@@ -272,25 +275,19 @@ function setup_stdio(stdio::Union{IOBuffer, BufferStream}, child_readable::Bool)
272275
@async try
273276
write(in, out)
274277
catch ex
275-
@warn "Process error" exception=(ex, catch_backtrace())
278+
@warn "Process I/O error" exception=(ex, catch_backtrace())
276279
finally
277280
close(parent)
278281
child_readable || closewrite(stdio)
279282
end
280283
end
281-
catch ex
284+
catch
282285
close_pipe_sync(child)
283-
rethrow(ex)
286+
rethrow()
284287
end
285288
return (child, true)
286289
end
287290

288-
function setup_stdio(io, child_readable::Bool)
289-
# if there is no specialization,
290-
# assume that rawhandle is defined for it
291-
return (io, false)
292-
end
293-
294291
close_stdio(stdio::OS_HANDLE) = close_pipe_sync(stdio)
295292
close_stdio(stdio) = close(stdio)
296293

base/stream.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ end
283283
lock(s::LibuvStream) = lock(s.lock)
284284
unlock(s::LibuvStream) = unlock(s.lock)
285285

286+
setup_stdio(stream::LibuvStream, ::Bool) = (stream, false)
286287
rawhandle(stream::LibuvStream) = stream.handle
287288
unsafe_convert(::Type{Ptr{Cvoid}}, s::Union{LibuvStream, LibuvServer}) = s.handle
288289

@@ -1488,6 +1489,7 @@ function close(s::BufferStream)
14881489
end
14891490
end
14901491
uvfinalize(s::BufferStream) = nothing
1492+
setup_stdio(stream::BufferStream, child_readable::Bool) = invoke(setup_stdio, Tuple{IO, Bool}, stream, child_readable)
14911493

14921494
function read(s::BufferStream, ::Type{UInt8})
14931495
nread = lock(s.cond) do

test/spawn.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,12 @@ let text = "input-test-text"
771771
@test proc.out === out
772772
@test read(out, String) == text
773773
@test success(proc)
774+
775+
out = PipeBuffer()
776+
proc = run(catcmd, IOBuffer(SubString(text)), out)
777+
@test success(proc)
778+
@test proc.out === proc.err === proc.in === devnull
779+
@test String(take!(out)) == text
774780
end
775781

776782

0 commit comments

Comments
 (0)