Skip to content

Commit

Permalink
Merge pull request #36323 from JuliaLang/teh/io_field_spec
Browse files Browse the repository at this point in the history
Improve inferrability of stream IO
  • Loading branch information
timholy authored Jun 23, 2020
2 parents e0babe8 + 44f88a8 commit be72a57
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 9 deletions.
21 changes: 20 additions & 1 deletion base/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,27 @@ function open(f::Function, args...; kwargs...)
end
end

# Generic wrappers around other IO objects
"""
AbstractPipe
`AbstractPipe` is the abstract supertype for IO pipes that provide for communication between processes.
If `pipe isa AbstractPipe`, it must obey the following interface:
- `pipe.in` or `pipe.in_stream`, if present, must be of type `IO` and be used to provide input to the pipe
- `pipe.out` or `pipe.out_stream`, if present, must be of type `IO` and be used for output from the pipe
- `pipe.err` or `pipe.err_stream`, if present, must be of type `IO` and be used for writing errors from the pipe
"""
abstract type AbstractPipe <: IO end

function getproperty(pipe::AbstractPipe, name::Symbol)
if name === :in || name === :in_stream || name === :out || name === :out_stream ||
name === :err || name === :err_stream
return getfield(pipe, name)::IO
end
return getfield(pipe, name)
end

function pipe_reader end
function pipe_writer end

Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ getindex(io::IO, key) = throw(KeyError(key))
get(io::IOContext, key, default) = get(io.dict, key, default)
get(io::IO, key, default) = default

displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize] : displaysize(io.io)
displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io)

show_circular(io::IO, @nospecialize(x)) = false
function show_circular(io::IOContext, @nospecialize(x))
Expand Down
72 changes: 65 additions & 7 deletions base/stream.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,67 @@ end

## types ##
abstract type IOServer end
"""
LibuvServer
An abstract type for IOServers handled by libuv.
If `server isa LibuvServer`, it must obey the following interface:
- `server.handle` must be a `Ptr{Cvoid}`
- `server.status` must be an `Int`
- `server.cond` must be a `GenericCondition`
"""
abstract type LibuvServer <: IOServer end

function getproperty(server::LibuvServer, name::Symbol)
if name === :handle
return getfield(server, :handle)::Ptr{Cvoid}
elseif name === :status
return getfield(server, :status)::Int
elseif name === :cond
return getfield(server, :cond)::GenericCondition
else
return getfield(server, name)
end
end

"""
LibuvStream
An abstract type for IO streams handled by libuv.
If`stream isa LibuvStream`, it must obey the following interface:
- `stream.handle`, if present, must be a `Ptr{Cvoid}`
- `stream.status`, if present, must be an `Int`
- `stream.buffer`, if present, must be an `IOBuffer`
- `stream.sendbuf`, if present, must be a `Union{Nothing,IOBuffer}`
- `stream.cond`, if present, must be a `GenericCondition`
- `stream.lock`, if present, must be an `AbstractLock`
- `stream.throttle`, if present, must be an `Int`
"""
abstract type LibuvStream <: IO end

function getproperty(stream::LibuvStream, name::Symbol)
if name === :handle
return getfield(stream, :handle)::Ptr{Cvoid}
elseif name === :status
return getfield(stream, :status)::Int
elseif name === :buffer
return getfield(stream, :buffer)::IOBuffer
elseif name === :sendbuf
return getfield(stream, :sendbuf)::Union{Nothing,IOBuffer}
elseif name === :cond
return getfield(stream, :cond)::GenericCondition
elseif name === :lock
return getfield(stream, :lock)::AbstractLock
elseif name === :throttle
return getfield(stream, :throttle)::Int
else
return getfield(stream, name)
end
end

# IO
# +- GenericIOBuffer{T<:AbstractArray{UInt8,1}} (not exported)
Expand Down Expand Up @@ -320,7 +378,7 @@ function isopen(x::Union{LibuvStream, LibuvServer})
if x.status == StatusUninit || x.status == StatusInit
throw(ArgumentError("$x is not initialized"))
end
return x.status::Int != StatusClosed && x.status::Int != StatusEOF
return x.status != StatusClosed && x.status != StatusEOF
end

function check_open(x::Union{LibuvStream, LibuvServer})
Expand Down Expand Up @@ -395,7 +453,7 @@ function close(stream::Union{LibuvStream, LibuvServer})
stream.status = StatusClosing
elseif isopen(stream) || stream.status == StatusEOF
should_wait = uv_handle_data(stream) != C_NULL
if stream.status::Int != StatusClosing
if stream.status != StatusClosing
ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle)
stream.status = StatusClosing
end
Expand All @@ -410,7 +468,7 @@ function uvfinalize(uv::Union{LibuvStream, LibuvServer})
iolock_begin()
if uv.handle != C_NULL
disassociate_julia_struct(uv.handle) # not going to call the usual close hooks
if uv.status::Int != StatusUninit
if uv.status != StatusUninit
close(uv)
else
Libc.free(uv.handle)
Expand Down Expand Up @@ -524,7 +582,7 @@ function uv_alloc_buf(handle::Ptr{Cvoid}, size::Csize_t, buf::Ptr{Cvoid})
stream = unsafe_pointer_to_objref(hd)::LibuvStream

local data::Ptr{Cvoid}, newsize::Csize_t
if stream.status::Int != StatusActive
if stream.status != StatusActive
data = C_NULL
newsize = 0
else
Expand Down Expand Up @@ -557,7 +615,7 @@ function uv_readcb(handle::Ptr{Cvoid}, nread::Cssize_t, buf::Ptr{Cvoid})
if isa(stream, TTY)
stream.status = StatusEOF # libuv called uv_stop_reading already
notify(stream.cond)
elseif stream.status::Int != StatusClosing
elseif stream.status != StatusClosing
# begin shutdown of the stream
ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle)
stream.status = StatusClosing
Expand Down Expand Up @@ -667,7 +725,7 @@ show(io::IO, stream::Pipe) = print(io,

function open_pipe!(p::PipeEndpoint, handle::OS_HANDLE)
iolock_begin()
if p.status::Int != StatusInit
if p.status != StatusInit
error("pipe is already in use or has been closed")
end
err = ccall(:uv_pipe_open, Int32, (Ptr{Cvoid}, OS_HANDLE), p.handle, handle)
Expand Down Expand Up @@ -1061,7 +1119,7 @@ _fd(x::Union{OS_HANDLE, RawFD}) = x

function _fd(x::Union{LibuvStream, LibuvServer})
fd = Ref{OS_HANDLE}(INVALID_OS_HANDLE)
if x.status::Int != StatusUninit && x.status::Int != StatusClosed
if x.status != StatusUninit && x.status != StatusClosed
err = ccall(:uv_fileno, Int32, (Ptr{Cvoid}, Ptr{OS_HANDLE}), x.handle, fd)
# handle errors by returning INVALID_OS_HANDLE
end
Expand Down

2 comments on commit be72a57

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily benchmark build, I will reply here when finished:

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan

Please sign in to comment.