Skip to content

Commit bc55f44

Browse files
authored
[release-1.9] Backports for Julia 1.9 (#50977)
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) -->
2 parents bed2cd5 + 518eb7f commit bc55f44

File tree

63 files changed

+436
-239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+436
-239
lines changed

Make.inc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,15 +581,15 @@ endif
581581

582582
ifeq ($(OS),WINNT)
583583
define versioned_libname
584-
$$(if $(2),$(1)-$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
584+
$(if $(2),$(1)-$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
585585
endef
586586
else ifeq ($(OS),Darwin)
587587
define versioned_libname
588-
$$(if $(2),$(1).$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
588+
$(if $(2),$(1).$(2).$(SHLIB_EXT),$(1).$(SHLIB_EXT))
589589
endef
590590
else
591591
define versioned_libname
592-
$$(if $(2),$(1).$(SHLIB_EXT).$(2),$(1).$(SHLIB_EXT))
592+
$(if $(2),$(1).$(SHLIB_EXT).$(2),$(1).$(SHLIB_EXT))
593593
endef
594594
endif
595595

base/channels.jl

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Channel(sz=0) = Channel{Any}(sz)
5959

6060
# special constructors
6161
"""
62-
Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false)
62+
Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing)
6363
6464
Create a new task from `func`, bind it to a new channel of type
6565
`T` and size `size`, and schedule the task, all in a single call.
@@ -70,9 +70,14 @@ The channel is automatically closed when the task terminates.
7070
If you need a reference to the created task, pass a `Ref{Task}` object via
7171
the keyword argument `taskref`.
7272
73-
If `spawn = true`, the Task created for `func` may be scheduled on another thread
73+
If `spawn=true`, the `Task` created for `func` may be scheduled on another thread
7474
in parallel, equivalent to creating a task via [`Threads.@spawn`](@ref).
7575
76+
If `spawn=true` and the `threadpool` argument is not set, it defaults to `:default`.
77+
78+
If the `threadpool` argument is set (to `:default` or `:interactive`), this implies
79+
that `spawn=true` and the new Task is spawned to the specified threadpool.
80+
7681
Return a `Channel`.
7782
7883
# Examples
@@ -117,6 +122,9 @@ true
117122
In earlier versions of Julia, Channel used keyword arguments to set `size` and `T`, but
118123
those constructors are deprecated.
119124
125+
!!! compat "Julia 1.9"
126+
The `threadpool=` argument was added in Julia 1.9.
127+
120128
```jldoctest
121129
julia> chnl = Channel{Char}(1, spawn=true) do ch
122130
for c in "hello world"
@@ -129,12 +137,18 @@ julia> String(collect(chnl))
129137
"hello world"
130138
```
131139
"""
132-
function Channel{T}(func::Function, size=0; taskref=nothing, spawn=false) where T
140+
function Channel{T}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing) where T
133141
chnl = Channel{T}(size)
134142
task = Task(() -> func(chnl))
143+
if threadpool === nothing
144+
threadpool = :default
145+
else
146+
spawn = true
147+
end
135148
task.sticky = !spawn
136149
bind(chnl, task)
137150
if spawn
151+
Threads._spawn_set_thrpool(task, threadpool)
138152
schedule(task) # start it on (potentially) another thread
139153
else
140154
yield(task) # immediately start it, yielding the current thread
@@ -149,17 +163,17 @@ Channel(func::Function, args...; kwargs...) = Channel{Any}(func, args...; kwargs
149163
# of course not deprecated.)
150164
# We use `nothing` default values to check which arguments were set in order to throw the
151165
# deprecation warning if users try to use `spawn=` with `ctype=` or `csize=`.
152-
function Channel(func::Function; ctype=nothing, csize=nothing, taskref=nothing, spawn=nothing)
166+
function Channel(func::Function; ctype=nothing, csize=nothing, taskref=nothing, spawn=nothing, threadpool=nothing)
153167
# The spawn= keyword argument was added in Julia v1.3, and cannot be used with the
154168
# deprecated keyword arguments `ctype=` or `csize=`.
155-
if (ctype !== nothing || csize !== nothing) && spawn !== nothing
156-
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!"))
169+
if (ctype !== nothing || csize !== nothing) && (spawn !== nothing || threadpool !== nothing)
170+
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!"))
157171
end
158172
# Set the actual default values for the arguments.
159173
ctype === nothing && (ctype = Any)
160174
csize === nothing && (csize = 0)
161175
spawn === nothing && (spawn = false)
162-
return Channel{ctype}(func, csize; taskref=taskref, spawn=spawn)
176+
return Channel{ctype}(func, csize; taskref=taskref, spawn=spawn, threadpool=threadpool)
163177
end
164178

165179
closed_exception() = InvalidStateException("Channel is closed.", :closed)

base/docs/basedocs.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,12 @@ Create a `Task` (i.e. coroutine) to execute the given function `func` (which
16921692
must be callable with no arguments). The task exits when this function returns.
16931693
The task will run in the "world age" from the parent at construction when [`schedule`](@ref)d.
16941694
1695+
!!! warning
1696+
By default tasks will have the sticky bit set to true `t.sticky`. This models the
1697+
historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread
1698+
they are first scheduled on. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky
1699+
bit manually to `false`.
1700+
16951701
# Examples
16961702
```jldoctest
16971703
julia> a() = sum(i for i in 1:1000);

base/file.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,9 @@ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false)
303303
try
304304
ret = ccall(:uv_fs_rmdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL)
305305
uv_fs_req_cleanup(req)
306-
ret < 0 && uv_error("rm($(repr(path)))", ret)
306+
if ret < 0 && !(force && ret == Base.UV_ENOENT)
307+
uv_error("rm($(repr(path)))", ret)
308+
end
307309
nothing
308310
finally
309311
Libc.free(req)

base/floatfuncs.jl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,20 @@ true
304304
function isapprox(x::Number, y::Number;
305305
atol::Real=0, rtol::Real=rtoldefault(x,y,atol),
306306
nans::Bool=false, norm::Function=abs)
307-
x == y || (isfinite(x) && isfinite(y) && norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))) || (nans && isnan(x) && isnan(y))
307+
x′, y′ = promote(x, y) # to avoid integer overflow
308+
x == y ||
309+
(isfinite(x) && isfinite(y) && norm(x-y) <= max(atol, rtol*max(norm(x′), norm(y′)))) ||
310+
(nans && isnan(x) && isnan(y))
311+
end
312+
313+
function isapprox(x::Integer, y::Integer;
314+
atol::Real=0, rtol::Real=rtoldefault(x,y,atol),
315+
nans::Bool=false, norm::Function=abs)
316+
if norm === abs && atol < 1 && rtol == 0
317+
return x == y
318+
else
319+
return norm(x - y) <= max(atol, rtol*max(norm(x), norm(y)))
320+
end
308321
end
309322

310323
"""

base/loading.jl

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,12 +2231,6 @@ function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, in
22312231

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

22412235
# prune the directory with cache files
22422236
if pkg.uuid !== nothing

base/partr.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ end
9595

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

@@ -131,6 +132,9 @@ function multiq_deletemin()
131132

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

136140
@label retry
@@ -182,6 +186,9 @@ end
182186
function multiq_check_empty()
183187
tid = Threads.threadid()
184188
tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1
189+
if tp == 0 # Foreign thread
190+
return true
191+
end
185192
for i = UInt32(1):length(heaps[tp])
186193
if heaps[tp][i].ntasks != 0
187194
return false

base/range.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -944,13 +944,13 @@ end
944944
# This is separate to make it useful even when running with --check-bounds=yes
945945
function unsafe_getindex(r::StepRangeLen{T}, i::Integer) where T
946946
i isa Bool && throw(ArgumentError("invalid index: $i of type Bool"))
947-
u = i - r.offset
947+
u = oftype(r.offset, i) - r.offset
948948
T(r.ref + u*r.step)
949949
end
950950

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

base/ryu/exp.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ function writeexp(buf, pos, v::T,
147147
end
148148
roundUp = 0
149149
if lastDigit != 5
150-
roundUp = lastDigit > 5
150+
roundUp = lastDigit > 5 ? 1 : 0
151151
else
152152
rexp = precision - e
153153
requiredTwos = -e2 - rexp

base/ryu/fixed.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function writefixed(buf, pos, v::T,
3838
mant = bits & MANTISSA_MASK
3939
exp = Int((bits >> 52) & EXP_MASK)
4040

41-
if exp == 0
41+
if exp == 0 # subnormal
4242
e2 = 1 - 1023 - 52
4343
m2 = mant
4444
else
@@ -53,7 +53,7 @@ function writefixed(buf, pos, v::T,
5353
i = len - 1
5454
while i >= 0
5555
j = p10bits - e2
56-
#=@inbounds=# mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1]
56+
mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1]
5757
digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8)
5858
if nonzero
5959
pos = append_nine_digits(digits, buf, pos)
@@ -103,7 +103,7 @@ function writefixed(buf, pos, v::T,
103103
end
104104
break
105105
end
106-
#=@inbounds=# mula, mulb, mulc = POW10_SPLIT_2[p + 1]
106+
mula, mulb, mulc = POW10_SPLIT_2[p + 1]
107107
digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8)
108108
if i < blocks - 1
109109
pos = append_nine_digits(digits, buf, pos)
@@ -118,11 +118,11 @@ function writefixed(buf, pos, v::T,
118118
k += 1
119119
end
120120
if lastDigit != 5
121-
roundUp = lastDigit > 5
121+
roundUp = lastDigit > 5 ? 1 : 0
122122
else
123123
requiredTwos = -e2 - precision - 1
124124
trailingZeros = requiredTwos <= 0 || (requiredTwos < 60 && pow2(m2, requiredTwos))
125-
roundUp = trailingZeros ? 2 : 1
125+
roundUp = trailingZeros ? 2 : 1 # 2 means round only if odd
126126
end
127127
if maximum > 0
128128
pos = append_c_digits(maximum, digits, buf, pos)
@@ -137,13 +137,13 @@ function writefixed(buf, pos, v::T,
137137
while true
138138
roundPos -= 1
139139
if roundPos == (startpos - 1) || (buf[roundPos] == UInt8('-')) || (plus && buf[roundPos] == UInt8('+')) || (space && buf[roundPos] == UInt8(' '))
140+
buf[pos] = UInt8('0')
140141
buf[roundPos + 1] = UInt8('1')
141142
if dotPos > 1
142143
buf[dotPos] = UInt8('0')
143144
buf[dotPos + 1] = decchar
144145
hasfractional = true
145146
end
146-
buf[pos] = UInt8('0')
147147
pos += 1
148148
break
149149
end

base/special/exp.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ function expm1(x::Float32)
458458
end
459459
x = Float64(x)
460460
N_float = round(x*Ln2INV(Float64))
461-
N = unsafe_trunc(UInt64, N_float)
461+
N = unsafe_trunc(Int64, N_float)
462462
r = muladd(N_float, Ln2(Float64), x)
463463
hi = evalpoly(r, (1.0, .5, 0.16666667546642386, 0.041666183019487026,
464464
0.008332997481506921, 0.0013966479175977883, 0.0002004037059220124))
@@ -475,7 +475,7 @@ function expm1(x::Float16)
475475
return Float16(x*evalpoly(x, (1f0, .5f0, 0.16666628f0, 0.04166785f0, 0.008351848f0, 0.0013675707f0)))
476476
end
477477
N_float = round(x*Ln2INV(Float32))
478-
N = unsafe_trunc(UInt32, N_float)
478+
N = unsafe_trunc(Int32, N_float)
479479
r = muladd(N_float, Ln2(Float32), x)
480480
hi = evalpoly(r, (1f0, .5f0, 0.16666667f0, 0.041665863f0, 0.008333111f0, 0.0013981499f0, 0.00019983904f0))
481481
small_part = r*hi

base/stream.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,6 @@ displaysize() = (parse(Int, get(ENV, "LINES", "24")),
565565
parse(Int, get(ENV, "COLUMNS", "80")))::Tuple{Int, Int}
566566

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

571570
local h::Int, w::Int
@@ -588,6 +587,7 @@ function displaysize(io::TTY)
588587
s1 = Ref{Int32}(0)
589588
s2 = Ref{Int32}(0)
590589
iolock_begin()
590+
check_open(io)
591591
Base.uv_error("size (TTY)", ccall(:uv_tty_get_winsize,
592592
Int32, (Ptr{Cvoid}, Ptr{Int32}, Ptr{Int32}),
593593
io, s1, s2) != 0)

base/strings/util.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ function lstrip(f, s::AbstractString)
350350
end
351351
lstrip(s::AbstractString) = lstrip(isspace, s)
352352
lstrip(s::AbstractString, chars::Chars) = lstrip(in(chars), s)
353+
lstrip(::AbstractString, ::AbstractString) = throw(ArgumentError("Both arguments are strings. The second argument should be a `Char` or collection of `Char`s"))
353354

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

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

415419
## string padding functions ##

base/task.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ function enq_work(t::Task)
783783
push!(workqueue_for(tid), t)
784784
else
785785
tp = Threads.threadpool(t)
786-
if Threads.threadpoolsize(tp) == 1
786+
if tp === :foreign || Threads.threadpoolsize(tp) == 1
787787
# There's only one thread in the task's assigned thread pool;
788788
# use its work queue.
789789
tid = (tp === :interactive) ? 1 : Threads.threadpoolsize(:interactive)+1

base/threadingconstructs.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ function _tpid_to_sym(tpid::Int8)
6363
return :interactive
6464
elseif tpid == 1
6565
return :default
66+
elseif tpid == -1
67+
return :foreign
6668
else
6769
throw(ArgumentError("Unrecognized threadpool id $tpid"))
6870
end
@@ -73,6 +75,8 @@ function _sym_to_tpid(tp::Symbol)
7375
return Int8(0)
7476
elseif tp === :default
7577
return Int8(1)
78+
elseif tp == :foreign
79+
return Int8(-1)
7680
else
7781
throw(ArgumentError("Unrecognized threadpool name `$(repr(tp))`"))
7882
end
@@ -81,7 +85,7 @@ end
8185
"""
8286
Threads.threadpool(tid = threadid()) -> Symbol
8387
84-
Returns the specified thread's threadpool; either `:default` or `:interactive`.
88+
Returns the specified thread's threadpool; either `:default`, `:interactive`, or `:foreign`.
8589
"""
8690
function threadpool(tid = threadid())
8791
tpid = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1)
@@ -108,6 +112,8 @@ See also: `BLAS.get_num_threads` and `BLAS.set_num_threads` in the
108112
function threadpoolsize(pool::Symbol = :default)
109113
if pool === :default || pool === :interactive
110114
tpid = _sym_to_tpid(pool)
115+
elseif pool == :foreign
116+
error("Threadpool size of `:foreign` is indeterminant")
111117
else
112118
error("invalid threadpool specified")
113119
end
@@ -143,7 +149,7 @@ function threading_run(fun, static)
143149
else
144150
# TODO: this should be the current pool (except interactive) if there
145151
# are ever more than two pools.
146-
ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default))
152+
@assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default)) == 1
147153
end
148154
tasks[i] = t
149155
schedule(t)
@@ -349,10 +355,10 @@ end
349355

350356
function _spawn_set_thrpool(t::Task, tp::Symbol)
351357
tpid = _sym_to_tpid(tp)
352-
if _nthreads_in_pool(tpid) == 0
358+
if tpid == -1 || _nthreads_in_pool(tpid) == 0
353359
tpid = _sym_to_tpid(:default)
354360
end
355-
ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid)
361+
@assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) == 1
356362
nothing
357363
end
358364

0 commit comments

Comments
 (0)