Skip to content

Commit

Permalink
[release-1.9] Backports for Julia 1.9 (#50977)
Browse files Browse the repository at this point in the history
Backported PRs:
- [x] #49357 <!-- Fix unclosed code fence in src/manual/methods.md -->
- [x] #50842 <!-- Avoid race conditions with recursive rm -->
- [x] #50858 <!-- Add a `threadpool` parameter to `Channel` constructor
-->
- [x] #50730 <!-- Fix integer overflow in `isapprox` -->
- [x] #50823 <!-- Make ranges more robust with unsigned indexes. -->
- [x] #50915 <!-- Add note the `Task` about sticky bit -->
- [x] #50989 <!-- fix incorrect results in `expm1(::Union{Float16,
Float32})` -->
- [x] #50912 <!-- Separate foreign threads into a :foreign threadpool
-->
- [x] #51019 <!-- fix a case of potentially use of undefined variable
when handling error in distributed message processing -->
- [x] #51222 <!-- Check again if the tty is open inside the IO lock -->
- [x] #51254 <!-- Ryu: make sure adding zeros does not overwrite
trailing dot -->
- [x] #51284 <!-- Avoid infinite loop when doing SIGTRAP in arm64-apple
-->
- [x] #51491 <!-- Throw clearer ArgumentError for strip with two string
args -->
- [x] #51531 <!-- fix `_tryonce_download_from_cache` (busybox.exe
download error) -->
  • Loading branch information
KristofferC committed Nov 7, 2023
2 parents bed2cd5 + 518eb7f commit bc55f44
Show file tree
Hide file tree
Showing 63 changed files with 436 additions and 239 deletions.
6 changes: 3 additions & 3 deletions Make.inc
Original file line number Diff line number Diff line change
Expand Up @@ -581,15 +581,15 @@ endif

ifeq ($(OS),WINNT)
define versioned_libname
$$(if $(2),$(1)-$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
$(if $(2),$(1)-$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
endef
else ifeq ($(OS),Darwin)
define versioned_libname
$$(if $(2),$(1).$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
$(if $(2),$(1).$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
endef
else
define versioned_libname
$$(if $(2),$(1).$(SHLIB_EXT).$(2),$(1).$(SHLIB_EXT))
$(if $(2),$(1).$(SHLIB_EXT).$(2),$(1).$(SHLIB_EXT))
endef
endif

Expand Down
28 changes: 21 additions & 7 deletions base/channels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Channel(sz=0) = Channel{Any}(sz)

# special constructors
"""
Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false)
Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing)
Create a new task from `func`, bind it to a new channel of type
`T` and size `size`, and schedule the task, all in a single call.
Expand All @@ -70,9 +70,14 @@ The channel is automatically closed when the task terminates.
If you need a reference to the created task, pass a `Ref{Task}` object via
the keyword argument `taskref`.
If `spawn = true`, the Task created for `func` may be scheduled on another thread
If `spawn=true`, the `Task` created for `func` may be scheduled on another thread
in parallel, equivalent to creating a task via [`Threads.@spawn`](@ref).
If `spawn=true` and the `threadpool` argument is not set, it defaults to `:default`.
If the `threadpool` argument is set (to `:default` or `:interactive`), this implies
that `spawn=true` and the new Task is spawned to the specified threadpool.
Return a `Channel`.
# Examples
Expand Down Expand Up @@ -117,6 +122,9 @@ true
In earlier versions of Julia, Channel used keyword arguments to set `size` and `T`, but
those constructors are deprecated.
!!! compat "Julia 1.9"
The `threadpool=` argument was added in Julia 1.9.
```jldoctest
julia> chnl = Channel{Char}(1, spawn=true) do ch
for c in "hello world"
Expand All @@ -129,12 +137,18 @@ julia> String(collect(chnl))
"hello world"
```
"""
function Channel{T}(func::Function, size=0; taskref=nothing, spawn=false) where T
function Channel{T}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing) where T
chnl = Channel{T}(size)
task = Task(() -> func(chnl))
if threadpool === nothing
threadpool = :default
else
spawn = true
end
task.sticky = !spawn
bind(chnl, task)
if spawn
Threads._spawn_set_thrpool(task, threadpool)
schedule(task) # start it on (potentially) another thread
else
yield(task) # immediately start it, yielding the current thread
Expand All @@ -149,17 +163,17 @@ Channel(func::Function, args...; kwargs...) = Channel{Any}(func, args...; kwargs
# of course not deprecated.)
# We use `nothing` default values to check which arguments were set in order to throw the
# deprecation warning if users try to use `spawn=` with `ctype=` or `csize=`.
function Channel(func::Function; ctype=nothing, csize=nothing, taskref=nothing, spawn=nothing)
function Channel(func::Function; ctype=nothing, csize=nothing, taskref=nothing, spawn=nothing, threadpool=nothing)
# The spawn= keyword argument was added in Julia v1.3, and cannot be used with the
# deprecated keyword arguments `ctype=` or `csize=`.
if (ctype !== nothing || csize !== nothing) && spawn !== nothing
throw(ArgumentError("Cannot set `spawn=` in the deprecated constructor `Channel(f; ctype=Any, csize=0)`. Please use `Channel{T=Any}(f, size=0; taskref=nothing, spawn=false)` instead!"))
if (ctype !== nothing || csize !== nothing) && (spawn !== nothing || threadpool !== nothing)
throw(ArgumentError("Cannot set `spawn=` or `threadpool=` in the deprecated constructor `Channel(f; ctype=Any, csize=0)`. Please use `Channel{T=Any}(f, size=0; taskref=nothing, spawn=false, threadpool=nothing)` instead!"))
end
# Set the actual default values for the arguments.
ctype === nothing && (ctype = Any)
csize === nothing && (csize = 0)
spawn === nothing && (spawn = false)
return Channel{ctype}(func, csize; taskref=taskref, spawn=spawn)
return Channel{ctype}(func, csize; taskref=taskref, spawn=spawn, threadpool=threadpool)
end

closed_exception() = InvalidStateException("Channel is closed.", :closed)
Expand Down
6 changes: 6 additions & 0 deletions base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,12 @@ Create a `Task` (i.e. coroutine) to execute the given function `func` (which
must be callable with no arguments). The task exits when this function returns.
The task will run in the "world age" from the parent at construction when [`schedule`](@ref)d.
!!! warning
By default tasks will have the sticky bit set to true `t.sticky`. This models the
historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread
they are first scheduled on. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky
bit manually to `false`.
# Examples
```jldoctest
julia> a() = sum(i for i in 1:1000);
Expand Down
4 changes: 3 additions & 1 deletion base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,9 @@ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false)
try
ret = ccall(:uv_fs_rmdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL)
uv_fs_req_cleanup(req)
ret < 0 && uv_error("rm($(repr(path)))", ret)
if ret < 0 && !(force && ret == Base.UV_ENOENT)
uv_error("rm($(repr(path)))", ret)
end
nothing
finally
Libc.free(req)
Expand Down
15 changes: 14 additions & 1 deletion base/floatfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,20 @@ true
function isapprox(x::Number, y::Number;
atol::Real=0, rtol::Real=rtoldefault(x,y,atol),
nans::Bool=false, norm::Function=abs)
x == y || (isfinite(x) && isfinite(y) && norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))) || (nans && isnan(x) && isnan(y))
x′, y′ = promote(x, y) # to avoid integer overflow
x == y ||
(isfinite(x) && isfinite(y) && norm(x-y) <= max(atol, rtol*max(norm(x′), norm(y′)))) ||
(nans && isnan(x) && isnan(y))
end

function isapprox(x::Integer, y::Integer;
atol::Real=0, rtol::Real=rtoldefault(x,y,atol),
nans::Bool=false, norm::Function=abs)
if norm === abs && atol < 1 && rtol == 0
return x == y
else
return norm(x - y) <= max(atol, rtol*max(norm(x), norm(y)))
end
end

"""
Expand Down
6 changes: 0 additions & 6 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2231,12 +2231,6 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in

# inherit permission from the source file (and make them writable)
chmod(tmppath, filemode(path) & 0o777 | 0o200)
if cache_objects
# Ensure that the user can execute the `.so` we're generating
# Note that on windows, `filemode(path)` typically returns `0o666`, so this
# addition of the execute bit for the user is doubly needed.
chmod(tmppath_so, filemode(path) & 0o777 | 0o333)
end

# prune the directory with cache files
if pkg.uuid !== nothing
Expand Down
7 changes: 7 additions & 0 deletions base/partr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ end

function multiq_insert(task::Task, priority::UInt16)
tpid = ccall(:jl_get_task_threadpoolid, Int8, (Any,), task)
@assert tpid > -1
heap_p = multiq_size(tpid)
tp = tpid + 1

Expand Down Expand Up @@ -131,6 +132,9 @@ function multiq_deletemin()

tid = Threads.threadid()
tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1
if tp == 0 # Foreign thread
return nothing
end
tpheaps = heaps[tp]

@label retry
Expand Down Expand Up @@ -182,6 +186,9 @@ end
function multiq_check_empty()
tid = Threads.threadid()
tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1
if tp == 0 # Foreign thread
return true
end
for i = UInt32(1):length(heaps[tp])
if heaps[tp][i].ntasks != 0
return false
Expand Down
4 changes: 2 additions & 2 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -944,13 +944,13 @@ end
# This is separate to make it useful even when running with --check-bounds=yes
function unsafe_getindex(r::StepRangeLen{T}, i::Integer) where T
i isa Bool && throw(ArgumentError("invalid index: $i of type Bool"))
u = i - r.offset
u = oftype(r.offset, i) - r.offset
T(r.ref + u*r.step)
end

function _getindex_hiprec(r::StepRangeLen, i::Integer) # without rounding by T
i isa Bool && throw(ArgumentError("invalid index: $i of type Bool"))
u = i - r.offset
u = oftype(r.offset, i) - r.offset
r.ref + u*r.step
end

Expand Down
2 changes: 1 addition & 1 deletion base/ryu/exp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function writeexp(buf, pos, v::T,
end
roundUp = 0
if lastDigit != 5
roundUp = lastDigit > 5
roundUp = lastDigit > 5 ? 1 : 0
else
rexp = precision - e
requiredTwos = -e2 - rexp
Expand Down
12 changes: 6 additions & 6 deletions base/ryu/fixed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function writefixed(buf, pos, v::T,
mant = bits & MANTISSA_MASK
exp = Int((bits >> 52) & EXP_MASK)

if exp == 0
if exp == 0 # subnormal
e2 = 1 - 1023 - 52
m2 = mant
else
Expand All @@ -53,7 +53,7 @@ function writefixed(buf, pos, v::T,
i = len - 1
while i >= 0
j = p10bits - e2
#=@inbounds=# mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1]
mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1]
digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8)
if nonzero
pos = append_nine_digits(digits, buf, pos)
Expand Down Expand Up @@ -103,7 +103,7 @@ function writefixed(buf, pos, v::T,
end
break
end
#=@inbounds=# mula, mulb, mulc = POW10_SPLIT_2[p + 1]
mula, mulb, mulc = POW10_SPLIT_2[p + 1]
digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8)
if i < blocks - 1
pos = append_nine_digits(digits, buf, pos)
Expand All @@ -118,11 +118,11 @@ function writefixed(buf, pos, v::T,
k += 1
end
if lastDigit != 5
roundUp = lastDigit > 5
roundUp = lastDigit > 5 ? 1 : 0
else
requiredTwos = -e2 - precision - 1
trailingZeros = requiredTwos <= 0 || (requiredTwos < 60 && pow2(m2, requiredTwos))
roundUp = trailingZeros ? 2 : 1
roundUp = trailingZeros ? 2 : 1 # 2 means round only if odd
end
if maximum > 0
pos = append_c_digits(maximum, digits, buf, pos)
Expand All @@ -137,13 +137,13 @@ function writefixed(buf, pos, v::T,
while true
roundPos -= 1
if roundPos == (startpos - 1) || (buf[roundPos] == UInt8('-')) || (plus && buf[roundPos] == UInt8('+')) || (space && buf[roundPos] == UInt8(' '))
buf[pos] = UInt8('0')
buf[roundPos + 1] = UInt8('1')
if dotPos > 1
buf[dotPos] = UInt8('0')
buf[dotPos + 1] = decchar
hasfractional = true
end
buf[pos] = UInt8('0')
pos += 1
break
end
Expand Down
4 changes: 2 additions & 2 deletions base/special/exp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ function expm1(x::Float32)
end
x = Float64(x)
N_float = round(x*Ln2INV(Float64))
N = unsafe_trunc(UInt64, N_float)
N = unsafe_trunc(Int64, N_float)
r = muladd(N_float, Ln2(Float64), x)
hi = evalpoly(r, (1.0, .5, 0.16666667546642386, 0.041666183019487026,
0.008332997481506921, 0.0013966479175977883, 0.0002004037059220124))
Expand All @@ -475,7 +475,7 @@ function expm1(x::Float16)
return Float16(x*evalpoly(x, (1f0, .5f0, 0.16666628f0, 0.04166785f0, 0.008351848f0, 0.0013675707f0)))
end
N_float = round(x*Ln2INV(Float32))
N = unsafe_trunc(UInt32, N_float)
N = unsafe_trunc(Int32, N_float)
r = muladd(N_float, Ln2(Float32), x)
hi = evalpoly(r, (1f0, .5f0, 0.16666667f0, 0.041665863f0, 0.008333111f0, 0.0013981499f0, 0.00019983904f0))
small_part = r*hi
Expand Down
2 changes: 1 addition & 1 deletion base/stream.jl
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,6 @@ displaysize() = (parse(Int, get(ENV, "LINES", "24")),
parse(Int, get(ENV, "COLUMNS", "80")))::Tuple{Int, Int}

function displaysize(io::TTY)
# A workaround for #34620 and #26687 (this still has the TOCTOU problem).
check_open(io)

local h::Int, w::Int
Expand All @@ -588,6 +587,7 @@ function displaysize(io::TTY)
s1 = Ref{Int32}(0)
s2 = Ref{Int32}(0)
iolock_begin()
check_open(io)
Base.uv_error("size (TTY)", ccall(:uv_tty_get_winsize,
Int32, (Ptr{Cvoid}, Ptr{Int32}, Ptr{Int32}),
io, s1, s2) != 0)
Expand Down
4 changes: 4 additions & 0 deletions base/strings/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ function lstrip(f, s::AbstractString)
end
lstrip(s::AbstractString) = lstrip(isspace, s)
lstrip(s::AbstractString, chars::Chars) = lstrip(in(chars), s)
lstrip(::AbstractString, ::AbstractString) = throw(ArgumentError("Both arguments are strings. The second argument should be a `Char` or collection of `Char`s"))

"""
rstrip([pred=isspace,] str::AbstractString) -> SubString
Expand Down Expand Up @@ -383,6 +384,8 @@ function rstrip(f, s::AbstractString)
end
rstrip(s::AbstractString) = rstrip(isspace, s)
rstrip(s::AbstractString, chars::Chars) = rstrip(in(chars), s)
rstrip(::AbstractString, ::AbstractString) = throw(ArgumentError("Both arguments are strings. The second argument should be a `Char` or collection of `Char`s"))


"""
strip([pred=isspace,] str::AbstractString) -> SubString
Expand Down Expand Up @@ -410,6 +413,7 @@ julia> strip("{3, 5}\\n", ['{', '}', '\\n'])
"""
strip(s::AbstractString) = lstrip(rstrip(s))
strip(s::AbstractString, chars::Chars) = lstrip(rstrip(s, chars), chars)
strip(::AbstractString, ::AbstractString) = throw(ArgumentError("Both arguments are strings. The second argument should be a `Char` or collection of `Char`s"))
strip(f, s::AbstractString) = lstrip(f, rstrip(f, s))

## string padding functions ##
Expand Down
2 changes: 1 addition & 1 deletion base/task.jl
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ function enq_work(t::Task)
push!(workqueue_for(tid), t)
else
tp = Threads.threadpool(t)
if Threads.threadpoolsize(tp) == 1
if tp === :foreign || Threads.threadpoolsize(tp) == 1
# There's only one thread in the task's assigned thread pool;
# use its work queue.
tid = (tp === :interactive) ? 1 : Threads.threadpoolsize(:interactive)+1
Expand Down
14 changes: 10 additions & 4 deletions base/threadingconstructs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ function _tpid_to_sym(tpid::Int8)
return :interactive
elseif tpid == 1
return :default
elseif tpid == -1
return :foreign
else
throw(ArgumentError("Unrecognized threadpool id $tpid"))
end
Expand All @@ -73,6 +75,8 @@ function _sym_to_tpid(tp::Symbol)
return Int8(0)
elseif tp === :default
return Int8(1)
elseif tp == :foreign
return Int8(-1)
else
throw(ArgumentError("Unrecognized threadpool name `$(repr(tp))`"))
end
Expand All @@ -81,7 +85,7 @@ end
"""
Threads.threadpool(tid = threadid()) -> Symbol
Returns the specified thread's threadpool; either `:default` or `:interactive`.
Returns the specified thread's threadpool; either `:default`, `:interactive`, or `:foreign`.
"""
function threadpool(tid = threadid())
tpid = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1)
Expand All @@ -108,6 +112,8 @@ See also: `BLAS.get_num_threads` and `BLAS.set_num_threads` in the
function threadpoolsize(pool::Symbol = :default)
if pool === :default || pool === :interactive
tpid = _sym_to_tpid(pool)
elseif pool == :foreign
error("Threadpool size of `:foreign` is indeterminant")
else
error("invalid threadpool specified")
end
Expand Down Expand Up @@ -143,7 +149,7 @@ function threading_run(fun, static)
else
# TODO: this should be the current pool (except interactive) if there
# are ever more than two pools.
ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default))
@assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default)) == 1
end
tasks[i] = t
schedule(t)
Expand Down Expand Up @@ -349,10 +355,10 @@ end

function _spawn_set_thrpool(t::Task, tp::Symbol)
tpid = _sym_to_tpid(tp)
if _nthreads_in_pool(tpid) == 0
if tpid == -1 || _nthreads_in_pool(tpid) == 0
tpid = _sym_to_tpid(:default)
end
ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid)
@assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) == 1
nothing
end

Expand Down
Loading

0 comments on commit bc55f44

Please sign in to comment.