diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 0000000000000..9359bd0ca70a6
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,3 @@
+# See https://github.com/actions/labeler
+port-to-master: '**'
+port-to-v1.10: '**'
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000000000..7ed825781c53b
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,14 @@
+
+## PR Description
+
+_What does this PR do?_
+
+## Checklist
+
+Requirements for merging:
+- [ ] I have opened an issue or PR upstream on JuliaLang/julia:
+- [ ] I have removed the `port-to-*` labels that don't apply.
+- [ ] I have opened a PR on raicode to test these changes:
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
new file mode 100644
index 0000000000000..2141a906e96cd
--- /dev/null
+++ b/.github/workflows/labeler.yml
@@ -0,0 +1,17 @@
+# See https://github.com/actions/labeler
+name: "Pull Request Labeler"
+on:
+ pull_request_target:
+ types:
+ - opened
+
+jobs:
+ triage:
+ permissions:
+ contents: read
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v4
+ with:
+ dot: true
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 0000000000000..3df2093491753
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,16 @@
+name: "Close stale PRs"
+on:
+ schedule:
+ - cron: "0 0 * * *" # every night at midnight
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@v8
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Comment or remove stale label, or this PR will be closed in 5 days.'
+ days-before-stale: 30
+ days-before-close: 5
+ stale-pr-label: 'stale'
diff --git a/.github/workflows/update-upstream-branches.yml b/.github/workflows/update-upstream-branches.yml
new file mode 100644
index 0000000000000..247000bbd42cd
--- /dev/null
+++ b/.github/workflows/update-upstream-branches.yml
@@ -0,0 +1,28 @@
+name: "Update upstream branches"
+on:
+ schedule:
+ - cron: "0 0 * * *" # every night at midnight
+ workflow_dispatch:
+
+jobs:
+ PullUpstream:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false # run all jobs in the matrix even if one fails
+ matrix:
+ branch:
+ - "master"
+ - "backports-release-1.10"
+ steps:
+ - name: Checkout RAI/julia
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ matrix.branch }}
+ - name: Update ${{ matrix.branch }}
+ run: |
+ git config --global user.email "julia-engineering@relational.ai"
+ git config --global user.name "RAI CI (GitHub Action Automation)"
+
+ git remote add upstream https://github.com/JuliaLang/julia
+ git pull upstream ${{ matrix.branch }}
+ git push origin ${{ matrix.branch }}
diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl
index b455f078d83eb..f27a68da44b90 100644
--- a/Compiler/src/abstractinterpretation.jl
+++ b/Compiler/src/abstractinterpretation.jl
@@ -2247,7 +2247,7 @@ function abstract_invoke(interp::AbstractInterpreter, arginfo::ArgInfo, si::Stmt
return Future(CallMeta(Bottom, ErrorException, EFFECTS_THROWS, NoCallInfo()))
end
# TODO: When we add curing, we may want to assume this is nothrow
- if (method_or_ci.owner === Nothing && method_ir_ci.def.def isa Method)
+ if (method_or_ci.owner === Nothing && method_or_ci.def.def isa Method)
exct = Union{exct, ErrorException}
end
update_valid_age!(sv, callee_valid_range)
diff --git a/Make.inc b/Make.inc
index c2ccf6757e3c9..a145980e0264c 100644
--- a/Make.inc
+++ b/Make.inc
@@ -381,7 +381,7 @@ $(foreach D,build_libdir build_private_libdir,$(eval $(call cache_rel_path,$(D),
# Save a special one: reverse_private_libdir_rel: usually just `../`, but good to be general:
reverse_private_libdir_rel_eval = $(call rel_path,$(private_libdir),$(libdir))
reverse_private_libdir_rel = $(call hit_cache,reverse_private_libdir_rel_eval)
-reverse_private_libexecdir_rel_eval = $(call rel_path,$(private_libexecdir),$(libdir))
+reverse_private_libexecdir_rel_eval = $(call rel_path,$(private_libexecdir),$(private_libdir))
reverse_private_libexecdir_rel = $(call hit_cache,reverse_private_libexecdir_rel_eval)
reverse_build_private_libexecdir_rel_eval = $(call rel_path,$(build_private_libexecdir),$(build_libdir))
reverse_build_private_libexecdir_rel = $(call hit_cache,reverse_build_private_libexecdir_rel_eval)
diff --git a/Makefile b/Makefile
index bae449b255a10..259113e430c41 100644
--- a/Makefile
+++ b/Makefile
@@ -485,6 +485,37 @@ else ifeq ($(JULIA_BUILD_MODE),debug)
$(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-internal-debug.$(SHLIB_EXT)
$(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN:$$ORIGIN/$(reverse_private_libdir_rel)' $(DESTDIR)$(private_libdir)/libjulia-codegen-debug.$(SHLIB_EXT)
endif
+endif
+
+ifeq ($(OS), Darwin)
+ifneq ($(DARWIN_FRAMEWORK),1)
+ for j in $(JL_PRIVATE_TOOLS) ; do \
+ [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \
+ install_name_tool -rpath @loader_path/$(build_libdir_rel) @executable_path/$(reverse_private_libexecdir_rel) $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \
+ done
+endif
+else ifneq (,$(findstring $(OS),Linux FreeBSD))
+ for j in $(JL_PRIVATE_TOOLS) ; do \
+ [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \
+ $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN/$(reverse_private_libexecdir_rel)' $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \
+ done
+endif
+
+ifneq ($(reverse_private_libexecdir_rel),$(reverse_build_private_libexecdir_rel))
+ifeq ($(OS), Darwin)
+ifneq ($(DARWIN_FRAMEWORK),1)
+ for j in $(JL_PRIVATE_EXES) ; do \
+ [ $$j = 7z ] && continue; \
+ [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \
+ install_name_tool -rpath @executable_path/$(reverse_build_private_libexecdir_rel) @executable_path/$(reverse_private_libexecdir_rel) $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \
+ done
+endif
+else ifneq (,$(findstring $(OS),Linux FreeBSD))
+ for j in $(JL_PRIVATE_EXES) ; do \
+ [ -L $(DESTDIR)$(private_libexecdir)/$$j ] && continue; \
+ $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN/$(reverse_private_libexecdir_rel)' $(DESTDIR)$(private_libexecdir)/$$j || exit 1; \
+ done
+endif
endif
# Fix rpaths for dependencies. This should be fixed in BinaryBuilder later.
diff --git a/NEWS.md b/NEWS.md
index f5f68e508bdd9..17dd8ced3d56c 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -8,7 +8,7 @@ New language features
entry points. Entry points can be marked using `Base.Experimental.entrypoint` ([#55047]). Not all
code is expected to work with this option, and since it is experimental you may encounter problems.
* Redefinition of constants is now well defined and follows world age semantics ([#57253]). Additional redefinitions
- (e.g. of types) are now allowed. See [the new manual chapter on world age](https://docs.julialang.org/en/v1.13-dev/manual/worldage/).
+ (e.g. of types) are now allowed. See [the new manual chapter on world age](https://docs.julialang.org/en/v1/manual/worldage/).
* A new keyword argument `usings::Bool` has been added to `names`, returning all names visible
via `using` ([#54609]).
* The `@atomic` macro family now supports reference assignment syntax, e.g. `@atomic :monotonic v[3] += 4`,
diff --git a/VERSION b/VERSION
index 6b89d58f861a7..032ff3c8bc26f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.12.2
+1.12.4+RAI
diff --git a/base/array.jl b/base/array.jl
index 665934b8a3a11..9674bc170ad6e 100644
--- a/base/array.jl
+++ b/base/array.jl
@@ -641,7 +641,7 @@ julia> collect(Float64, 1:2:5)
collect(::Type{T}, itr) where {T} = _collect(T, itr, IteratorSize(itr))
_collect(::Type{T}, itr, isz::Union{HasLength,HasShape}) where {T} =
- copyto!(_array_for(T, isz, _similar_shape(itr, isz)), itr)
+ copyto!(_array_for_inner(T, isz, _similar_shape(itr, isz)), itr)
function _collect(::Type{T}, itr, isz::SizeUnknown) where T
a = Vector{T}()
for x in itr
@@ -665,12 +665,12 @@ _similar_for(c::AbstractArray, ::Type{T}, itr, ::HasShape, axs) where {T} =
similar(c, T, axs)
# make a collection appropriate for collecting `itr::Generator`
-_array_for(::Type{T}, ::SizeUnknown, ::Nothing) where {T} = Vector{T}(undef, 0)
-_array_for(::Type{T}, ::HasLength, len::Integer) where {T} = Vector{T}(undef, Int(len))
-_array_for(::Type{T}, ::HasShape{N}, axs) where {T,N} = similar(Array{T,N}, axs)
+_array_for_inner(::Type{T}, ::SizeUnknown, ::Nothing) where {T} = Vector{T}(undef, 0)
+_array_for_inner(::Type{T}, ::HasLength, len::Integer) where {T} = Vector{T}(undef, Int(len))
+_array_for_inner(::Type{T}, ::HasShape{N}, axs) where {T,N} = similar(Array{T,N}, axs)
# used by syntax lowering for simple typed comprehensions
-_array_for(::Type{T}, itr, isz) where {T} = _array_for(T, isz, _similar_shape(itr, isz))
+_array_for(::Type{T}, itr, isz) where {T} = _array_for_inner(T, isz, _similar_shape(itr, isz))
"""
@@ -789,10 +789,10 @@ function collect(itr::Generator)
shp = _similar_shape(itr, isz)
y = iterate(itr)
if y === nothing
- return _array_for(et, isz, shp)
+ return _array_for_inner(et, isz, shp)
end
v1, st = y
- dest = _array_for(typeof(v1), isz, shp)
+ dest = _array_for_inner(typeof(v1), isz, shp)
# The typeassert gives inference a helping hand on the element type and dimensionality
# (work-around for #28382)
et′ = et <: Type ? Type : et
diff --git a/base/bitarray.jl b/base/bitarray.jl
index 93fa48c56e379..2b73b1ea8a710 100644
--- a/base/bitarray.jl
+++ b/base/bitarray.jl
@@ -151,16 +151,14 @@ function copy_chunks!(dest::Vector{UInt64}, pos_d::Int, src::Vector{UInt64}, pos
delta_ks = ks1 - ks0
u = _msk64
+ msk_d0 = ~(u << ld0)
+ msk_d1 = (u << (ld1+1))
if delta_kd == 0
- msk_d0 = ~(u << ld0) | (u << (ld1+1))
- else
- msk_d0 = ~(u << ld0)
- msk_d1 = (u << (ld1+1))
+ msk_d0 |= msk_d1
end
+ msk_s0 = (u << ls0)
if delta_ks == 0
- msk_s0 = (u << ls0) & ~(u << (ls1+1))
- else
- msk_s0 = (u << ls0)
+ msk_s0 &= ~(u << (ls1+1))
end
chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0)
@@ -211,16 +209,14 @@ function copy_chunks_rtol!(chunks::Vector{UInt64}, pos_d::Int, pos_s::Int, numbi
delta_kd = kd1 - kd0
delta_ks = ks1 - ks0
+ msk_d0 = ~(u << ld0)
+ msk_d1 = (u << (ld1+1))
if delta_kd == 0
- msk_d0 = ~(u << ld0) | (u << (ld1+1))
- else
- msk_d0 = ~(u << ld0)
- msk_d1 = (u << (ld1+1))
+ msk_d0 |= msk_d1
end
+ msk_s0 = (u << ls0)
if delta_ks == 0
- msk_s0 = (u << ls0) & ~(u << (ls1+1))
- else
- msk_s0 = (u << ls0)
+ msk_s0 &= ~(u << (ls1+1))
end
chunk_s0 = glue_src_bitchunks(chunks, ks0, ks1, msk_s0, ls0) & ~(u << s)
@@ -246,11 +242,10 @@ function fill_chunks!(Bc::Array{UInt64}, x::Bool, pos::Int, numbits::Int)
k1, l1 = get_chunks_id(pos+numbits-1)
u = _msk64
+ msk0 = (u << l0)
+ msk1 = ~(u << (l1+1))
if k1 == k0
- msk0 = (u << l0) & ~(u << (l1+1))
- else
- msk0 = (u << l0)
- msk1 = ~(u << (l1+1))
+ msk0 &= msk1
end
@inbounds if x
Bc[k0] |= msk0
diff --git a/base/file.jl b/base/file.jl
index ce9bc76256fd3..7c2d8ebe86de8 100644
--- a/base/file.jl
+++ b/base/file.jl
@@ -1267,22 +1267,19 @@ function rename(oldpath::AbstractString, newpath::AbstractString)
end
function sendfile(src::AbstractString, dst::AbstractString)
- src_open = false
- dst_open = false
- local src_file, dst_file
+ src_file = nothing
+ dst_file = nothing
try
src_file = open(src, JL_O_RDONLY)
- src_open = true
dst_file = open(dst, JL_O_CREAT | JL_O_TRUNC | JL_O_WRONLY, filemode(src_file))
- dst_open = true
bytes = filesize(stat(src_file))
sendfile(dst_file, src_file, Int64(0), Int(bytes))
finally
- if src_open && isopen(src_file)
+ if src_file !== nothing && isopen(src_file)
close(src_file)
end
- if dst_open && isopen(dst_file)
+ if dst_file !== nothing && isopen(dst_file)
close(dst_file)
end
end
diff --git a/base/loading.jl b/base/loading.jl
index 40db7df679b60..a804cc5f54706 100644
--- a/base/loading.jl
+++ b/base/loading.jl
@@ -4211,6 +4211,20 @@ function expand_compiler_path(tup)
end
compiler_chi(tup::Tuple) = CacheHeaderIncludes(expand_compiler_path(tup))
+"""
+ isprecompilable(f, argtypes::Tuple{Vararg{Any}})
+
+Check, as far as is possible without actually compiling, if the given
+function `f` can be compiled for the argument tuple (of types) `argtypes`.
+"""
+function isprecompilable(@nospecialize(f), @nospecialize(argtypes::Tuple))
+ isprecompilable(Tuple{Core.Typeof(f), argtypes...})
+end
+
+function isprecompilable(@nospecialize(argt::Type))
+ ccall(:jl_is_compilable, Int32, (Any,), argt) != 0
+end
+
"""
precompile(f, argtypes::Tuple{Vararg{Any}})
diff --git a/base/lock.jl b/base/lock.jl
index e74da826900a6..1feea348e5ac4 100644
--- a/base/lock.jl
+++ b/base/lock.jl
@@ -366,7 +366,7 @@ This is similar to using [`lock`](@ref) with a `do` block, but avoids creating a
and thus can improve the performance.
!!! compat
- `@lock` was added in Julia 1.3, and exported in Julia 1.10.
+ `@lock` was added in Julia 1.3, and exported in Julia 1.7.
"""
macro lock(l, expr)
quote
diff --git a/base/logging/logging.jl b/base/logging/logging.jl
index c44e74ffa627b..bb24a0e47b05b 100644
--- a/base/logging/logging.jl
+++ b/base/logging/logging.jl
@@ -129,6 +129,8 @@ end
LogLevel(level::LogLevel) = level
isless(a::LogLevel, b::LogLevel) = isless(a.level, b.level)
+isless(a::LogLevel, b::Integer) = isless(a.level, b)
+isless(a::Integer, b::LogLevel) = isless(a, b.level)
+(level::LogLevel, inc::Integer) = LogLevel(level.level+inc)
-(level::LogLevel, inc::Integer) = LogLevel(level.level-inc)
convert(::Type{LogLevel}, level::Integer) = LogLevel(level)
diff --git a/base/options.jl b/base/options.jl
index 1692c765443f3..b5730994492aa 100644
--- a/base/options.jl
+++ b/base/options.jl
@@ -64,6 +64,7 @@ struct JLOptions
trim::Int8
task_metrics::Int8
timeout_for_safepoint_straggler_s::Int16
+ safe_crash_log_file::Ptr{UInt8}
end
# This runs early in the sysimage != is not defined yet
diff --git a/base/partr.jl b/base/partr.jl
index d488330f0c87e..671c88fd6e786 100644
--- a/base/partr.jl
+++ b/base/partr.jl
@@ -97,7 +97,7 @@ function multiq_sift_down(heap::taskheap, idx::Int32)
child = Int(child)
child > length(heap.tasks) && break
if isassigned(heap.tasks, child) &&
- heap.tasks[child].priority < heap.tasks[idx].priority
+ heap.tasks[child].priority <= heap.tasks[idx].priority
t = heap.tasks[idx]
heap.tasks[idx] = heap.tasks[child]
heap.tasks[child] = t
diff --git a/base/process.jl b/base/process.jl
index fbc4acfd83e80..1a2324cb5d86e 100644
--- a/base/process.jl
+++ b/base/process.jl
@@ -131,15 +131,14 @@ end
if err == 0
pp = Process(cmd, handle, syncd)
associate_julia_struct(handle, pp)
+ iolock_end()
+ return pp
else
ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), handle) # will call free on handle eventually
+ iolock_end()
+ throw(_UVError("could not spawn " * repr(cmd), err))
end
- iolock_end()
end
- if err != 0
- throw(_UVError("could not spawn " * repr(cmd), err))
- end
- return pp
end
_spawn(cmds::AbstractCmd) = _spawn(cmds, SpawnIOs())
diff --git a/base/toml_parser.jl b/base/toml_parser.jl
index 378322e8cba95..46208eeff4195 100644
--- a/base/toml_parser.jl
+++ b/base/toml_parser.jl
@@ -716,8 +716,8 @@ function parse_array(l::Parser{Dates})::Err{Vector} where Dates
copyto_typed!(new, array)
elseif T === Union{}
new = Any[]
- elseif (T === TOMLDict) || (T == BigInt) || (T === UInt128) || (T === Int128) || (T <: Vector) ||
- (T === Dates.Date) || (T === Dates.Time) || (T === Dates.DateTime)
+ elseif (T === TOMLDict) || (T === BigInt) || (T === UInt128) || (T === Int128) || (T <: Vector) ||
+ (Dates !== nothing && ((T === Dates.Date) || (T === Dates.Time) || (T === Dates.DateTime)))
# do nothing, leave as Vector{Any}
new = array
else @assert false end
diff --git a/contrib/mac/app/Makefile b/contrib/mac/app/Makefile
index 70436a857c265..81b7e47cdf2cf 100644
--- a/contrib/mac/app/Makefile
+++ b/contrib/mac/app/Makefile
@@ -47,8 +47,8 @@ dmg/$(APP_NAME): startup.applescript julia.icns
plutil -insert CFBundleVersion -string "$(JULIA_VERSION_OPT_COMMIT)" $@/Contents/Info.plist
plutil -insert NSHumanReadableCopyright -string "$(APP_COPYRIGHT)" $@/Contents/Info.plist
-mkdir -p $@/Contents/Resources/julia
- $(MAKE) -C $(JULIAHOME) binary-dist
- $(TAR) -xzf $(JULIAHOME)/$(JULIA_BINARYDIST_FILENAME).tar.gz -C $@/Contents/Resources/julia --strip-components 1
+ make -C $(JULIAHOME) binary-dist
+ tar zxf $(JULIAHOME)/$(JULIA_BINARYDIST_FILENAME).tar.gz -C $@/Contents/Resources/julia --strip-components 1
find $@/Contents/Resources/julia -type f -exec chmod -w {} \;
# Even though the tarball may already be signed, we re-sign here to make it easier to add
# unsigned executables (like the app launcher) and whatnot, without needing to maintain lists
diff --git a/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/md5 b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/md5
new file mode 100644
index 0000000000000..cbd119f9a07e5
--- /dev/null
+++ b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/md5
@@ -0,0 +1 @@
+c8fea09ab0a67dcd9bacc413a008056b
diff --git a/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/sha512 b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/sha512
new file mode 100644
index 0000000000000..01c026a488114
--- /dev/null
+++ b/deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/sha512
@@ -0,0 +1 @@
+f44e5518d45d568a56116fcef058c006adefff6752b7bfecd29afffa7fc797739271bde2d3a6d0813d89973c862341d5ee47347a933146a43108ea716b133672
diff --git a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/md5 b/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/md5
deleted file mode 100644
index 0981743e0907a..0000000000000
--- a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/md5
+++ /dev/null
@@ -1 +0,0 @@
-1cf83c7ea8935ac0804e07ebe8ce7506
diff --git a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/sha512 b/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/sha512
deleted file mode 100644
index 971b146eecf64..0000000000000
--- a/deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/sha512
+++ /dev/null
@@ -1 +0,0 @@
-ec3edd7208e25fb1af0b9cb2324abbfb66e556442b75df619ffdf428f1d59f851055117db896fd3514352a32c34c4c42cf50e1386deca4ebd051de98369d5a3e
diff --git a/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5 b/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5
deleted file mode 100644
index 48b17f7ee32d2..0000000000000
--- a/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5
+++ /dev/null
@@ -1 +0,0 @@
-5c898e09839cfa16f80940b08bdebe9f
diff --git a/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512 b/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512
deleted file mode 100644
index 7236505c4e9f1..0000000000000
--- a/deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512
+++ /dev/null
@@ -1 +0,0 @@
-5202b09eaf172291260bc69a28944986c80a4362a9ef521b05c8ce95c69c9909439b5b2f3a52326ab820f1eff2e1ba2a8ddaa90080d9e29fd2f6e226856bda3e
diff --git a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5
new file mode 100644
index 0000000000000..fbc81b025a8f9
--- /dev/null
+++ b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5
@@ -0,0 +1 @@
+9fbd2f2f522bf0b5fd0d6b40f722db49
diff --git a/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512
new file mode 100644
index 0000000000000..576ad3c62915e
--- /dev/null
+++ b/deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512
@@ -0,0 +1 @@
+77e2f3729e58b74cfdd1fc2d991fc6552e7f7ceb7b9fdc837e5885932e2f15d6ab3c76b6ab90b1a6c66050dab7279abc0b976ee52b840b757dd13b1a955f5c5c
diff --git a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5 b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5
deleted file mode 100644
index ab3e4ebfe9f25..0000000000000
--- a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5
+++ /dev/null
@@ -1 +0,0 @@
-eadaa92895c8d4d33eb601165ef765d5
diff --git a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512 b/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512
deleted file mode 100644
index 7a250e67b00d3..0000000000000
--- a/deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512
+++ /dev/null
@@ -1 +0,0 @@
-bb37377b360eca1a32c78b1d11b83c7e918a8ddb9df79388694b6f415dc5d5cf6182df7437869b3970011e5dcda4a3f821b58498bfa6fd7df697fcd51383ca12
diff --git a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5
new file mode 100644
index 0000000000000..372c4735d1c07
--- /dev/null
+++ b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5
@@ -0,0 +1 @@
+230a4d9544d2cec7c4adf9a50d228345
diff --git a/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512
new file mode 100644
index 0000000000000..6f1838c5bb750
--- /dev/null
+++ b/deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512
@@ -0,0 +1 @@
+d66a3b6e032af45b78f71d8c5851c3e6fe1c5159f4c4428b64f1f6a52a111a488a9cec8f702a9e5de96593f5d4735f7490ae6c33eee7155b31f743b82aebd38b
diff --git a/deps/checksums/cacert-2025-11-04.pem/md5 b/deps/checksums/cacert-2025-11-04.pem/md5
new file mode 100644
index 0000000000000..641a98aecef02
--- /dev/null
+++ b/deps/checksums/cacert-2025-11-04.pem/md5
@@ -0,0 +1 @@
+4ca8e1c3e8fc44c3ecd7a1fb9d3a6d03
diff --git a/deps/checksums/cacert-2025-11-04.pem/sha512 b/deps/checksums/cacert-2025-11-04.pem/sha512
new file mode 100644
index 0000000000000..bbd48b9475d7f
--- /dev/null
+++ b/deps/checksums/cacert-2025-11-04.pem/sha512
@@ -0,0 +1 @@
+9d9f7ecc829bafc222501d8a66852d96a51f522b04a82963e4166c87b85d6a5e5eedb50ced2ef3026cd7cb06fcb4b7dca59c4157813a067cb7b185e32f2957ec
diff --git a/deps/libgit2.mk b/deps/libgit2.mk
index dd0d883c2e736..8b17ae6d70424 100644
--- a/deps/libgit2.mk
+++ b/deps/libgit2.mk
@@ -4,7 +4,6 @@ ifneq ($(USE_BINARYBUILDER_LIBGIT2),1)
LIBGIT2_GIT_URL := https://github.com/libgit2/libgit2.git
LIBGIT2_TAR_URL = https://api.github.com/repos/libgit2/libgit2/tarball/$1
$(eval $(call git-external,libgit2,LIBGIT2,CMakeLists.txt,,$(SRCCACHE)))
-$(SRCCACHE)/$(LIBGIT2_SRC_DIR)/source-extracted: $(MSYS_NONEXISTENT_SYMLINK_TARGET_FIX)
ifeq ($(USE_SYSTEM_LIBSSH2), 0)
$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/libssh2
@@ -14,15 +13,7 @@ ifeq ($(USE_SYSTEM_OPENSSL), 0)
$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/openssl
endif
-ifeq ($(USE_SYSTEM_PCRE), 0)
-$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/pcre
-endif
-
-ifeq ($(USE_SYSTEM_ZLIB), 0)
-$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/zlib
-endif
-
-LIBGIT2_OPTS := $(CMAKE_COMMON) -DCMAKE_BUILD_TYPE=Release -DUSE_THREADS=ON -DUSE_BUNDLED_ZLIB=OFF -DUSE_SSH=ON -DREGEX_BACKEND=pcre2 -DBUILD_CLI=OFF -DBUILD_TESTS=OFF
+LIBGIT2_OPTS := $(CMAKE_COMMON) -DCMAKE_BUILD_TYPE=Release -DUSE_THREADS=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON -DBUILD_CLI=OFF
ifeq ($(OS),WINNT)
LIBGIT2_OPTS += -DWIN32=ON -DMINGW=ON
ifeq ($(USE_SYSTEM_LIBSSH2), 0)
diff --git a/deps/libgit2.version b/deps/libgit2.version
index ffba640e3b24e..5a7fb591098bb 100644
--- a/deps/libgit2.version
+++ b/deps/libgit2.version
@@ -11,4 +11,4 @@ LIBGIT2_SHA1=338e6fb681369ff0537719095e22ce9dc602dbf0
# The versions of cacert.pem are identified by the date (YYYY-MM-DD) of their changes.
# See https://curl.haxx.se/docs/caextract.html for more details.
# Keep in sync with `stdlib/MozillaCACerts_jll/Project.toml`.
-MOZILLA_CACERT_VERSION := 2025-05-20
+MOZILLA_CACERT_VERSION := 2025-11-04
diff --git a/deps/libssh2.mk b/deps/libssh2.mk
index 05cc12b6e159b..661e26516c828 100644
--- a/deps/libssh2.mk
+++ b/deps/libssh2.mk
@@ -17,6 +17,9 @@ endif
ifeq ($(OS),WINNT)
LIBSSH2_OPTS += -DCRYPTO_BACKEND=WinCNG -DENABLE_ZLIB_COMPRESSION=OFF
+ifeq ($(BUILD_OS),WINNT)
+LIBSSH2_OPTS += -G"MSYS Makefiles"
+endif
else
LIBSSH2_OPTS += -DCRYPTO_BACKEND=OpenSSL -DENABLE_ZLIB_COMPRESSION=OFF
LIBSSH2_OPTS += -DOPENSSL_ROOT_DIR=$(build_prefix)
@@ -35,7 +38,7 @@ LIBSSH2_SRC_PATH := $(SRCCACHE)/$(LIBSSH2_SRC_DIR)
$(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured: $(LIBSSH2_SRC_PATH)/source-extracted
mkdir -p $(dir $@)
cd $(dir $@) && \
- $(CMAKE) $(CMAKE_GENERATOR_COMMAND) $(dir $<) $(LIBSSH2_OPTS)
+ $(CMAKE) $(dir $<) $(LIBSSH2_OPTS)
echo 1 > $@
$(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-compiled: $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured
diff --git a/deps/libuv.mk b/deps/libuv.mk
index 993aa4fc144da..eacabac55e34f 100644
--- a/deps/libuv.mk
+++ b/deps/libuv.mk
@@ -4,7 +4,7 @@ LIBUV_GIT_URL:=https://github.com/JuliaLang/libuv.git
LIBUV_TAR_URL=https://api.github.com/repos/JuliaLang/libuv/tarball/$1
$(eval $(call git-external,libuv,LIBUV,configure,,$(SRCCACHE)))
-UV_CFLAGS := -O2 -DBUILDING_UV_SHARED=1
+UV_CFLAGS := -O2
UV_FLAGS := LDFLAGS="$(LDFLAGS) $(CLDFLAGS) -v"
UV_FLAGS += CFLAGS="$(CFLAGS) $(UV_CFLAGS) $(SANITIZE_OPTS)"
diff --git a/deps/llvm.mk b/deps/llvm.mk
index 8b823b8664bf2..09dd4f187d611 100644
--- a/deps/llvm.mk
+++ b/deps/llvm.mk
@@ -7,14 +7,6 @@ ifneq ($(USE_BINARYBUILDER_LLVM), 1)
LLVM_GIT_URL:=https://github.com/JuliaLang/llvm-project.git
LLVM_TAR_URL=https://api.github.com/repos/JuliaLang/llvm-project/tarball/$1
$(eval $(call git-external,llvm,LLVM,CMakeLists.txt,,$(SRCCACHE)))
-# LLVM's tarball contains symlinks to non-existent targets. This breaks the
-# the default msys strategy `deepcopy` symlink strategy. To workaround this,
-# switch to `native` which tries native windows symlinks (possible if the
-# machine is in developer mode) - or if not, falls back to cygwin-style
-# symlinks. We don't particularly care either way - we just need to symlinks
-# to succeed. We could guard this by a uname check, but it's harmless elsewhere,
-# so let's not incur the additional overhead.
-$(SRCCACHE)/$(LLVM_SRC_DIR)/source-extracted: $(MSYS_NONEXISTENT_SYMLINK_TARGET_FIX)
LLVM_BUILDDIR := $(BUILDDIR)/$(LLVM_SRC_DIR)
LLVM_BUILDDIR_withtype := $(LLVM_BUILDDIR)/build_$(LLVM_BUILDTYPE)
diff --git a/deps/openblas.mk b/deps/openblas.mk
index e5a988ba84df2..80881a6a13c4e 100644
--- a/deps/openblas.mk
+++ b/deps/openblas.mk
@@ -44,7 +44,7 @@ OPENBLAS_CFLAGS := -O2
# Decide whether to build for 32-bit or 64-bit arch
ifneq ($(XC_HOST),)
-OPENBLAS_BUILD_OPTS += OSNAME=$(OS) CROSS=1 HOSTCC=$(HOSTCC) CROSS_SUFFIX=$(CROSS_COMPILE)
+OPENBLAS_BUILD_OPTS += OSNAME=$(OS) CROSS=1 HOSTCC="$(HOSTCC)" CROSS_SUFFIX=$(CROSS_COMPILE)
endif
ifeq ($(OS),WINNT)
ifneq ($(ARCH),x86_64)
diff --git a/deps/openssl.mk b/deps/openssl.mk
index 734ddb3274e78..705303432c2c6 100644
--- a/deps/openssl.mk
+++ b/deps/openssl.mk
@@ -59,7 +59,7 @@ $(BUILDDIR)/openssl-$(OPENSSL_VER)/build-configured: $(SRCCACHE)/openssl-$(OPENS
mkdir -p $(dir $@)
cd $(dir $@) && \
CC="$(CC) $(SANITIZE_OPTS)" CXX="$(CXX) $(SANITIZE_OPTS)" LDFLAGS="$(LDFLAGS) $(RPATH_ESCAPED_ORIGIN) $(SANITIZE_LDFLAGS)" \
- $(dir $<)/Configure shared --prefix=$(abspath $(build_prefix)) --libdir=$(abspath $(build_libdir)) $(OPENSSL_TARGET)
+ $(dir $<)/Configure shared --prefix=$(abspath $(build_prefix)) $(OPENSSL_TARGET)
echo 1 > $@
$(BUILDDIR)/openssl-$(OPENSSL_VER)/build-compiled: $(BUILDDIR)/openssl-$(OPENSSL_VER)/build-configured
@@ -75,16 +75,18 @@ endif
# Override bindir and only install runtime libraries, otherwise they'll go into build_depsbindir.
OPENSSL_INSTALL = \
mkdir -p $2$$(build_shlibdir) && \
- $$(MAKE) -C $1 install_runtime_libs $$(MAKE_COMMON) bindir=$$(build_shlibdir) $3 DESTDIR="$2"
+ $$(MAKE) -C $1 install_dev $$(MAKE_COMMON) bindir=$$(build_shlibdir) $3 DESTDIR="$2"
+
+OPENSSL_POST_INSTALL := \
+ $(WIN_MAKE_HARD_LINK) $(build_bindir)/libcrypto-*.dll $(build_bindir)/libcrypto.dll && \
+ $(WIN_MAKE_HARD_LINK) $(build_bindir)/libssl-*.dll $(build_bindir)/libssl.dll && \
+ $(INSTALL_NAME_CMD)libcrypto.$(SHLIB_EXT) $(build_shlibdir)/libcrypto.$(SHLIB_EXT) && \
+ $(INSTALL_NAME_CMD)libssl.$(SHLIB_EXT) $(build_shlibdir)/libssl.$(SHLIB_EXT) && \
+ $(INSTALL_NAME_CHANGE_CMD) $(build_shlibdir)/libcrypto.3.dylib @rpath/libcrypto.$(SHLIB_EXT) $(build_shlibdir)/libssl.$(SHLIB_EXT)
$(eval $(call staged-install, \
openssl,openssl-$(OPENSSL_VER), \
- OPENSSL_INSTALL,,, \
- $$(WIN_MAKE_HARD_LINK) $(build_bindir)/libcrypto-*.dll $(build_bindir)/libcrypto.dll && \
- $$(WIN_MAKE_HARD_LINK) $(build_bindir)/libssl-*.dll $(build_bindir)/libssl.dll && \
- $$(INSTALL_NAME_CMD)libcrypto.$$(SHLIB_EXT) $$(build_shlibdir)/libcrypto.$$(SHLIB_EXT) && \
- $$(INSTALL_NAME_CMD)libssl.$$(SHLIB_EXT) $$(build_shlibdir)/libssl.$$(SHLIB_EXT) && \
- $$(INSTALL_NAME_CHANGE_CMD) $$(build_shlibdir)/libcrypto.3.dylib @rpath/libcrypto.$$(SHLIB_EXT) $$(build_shlibdir)/libssl.$$(SHLIB_EXT)))
+ OPENSSL_INSTALL,,,$(OPENSSL_POST_INSTALL)))
clean-openssl:
-rm -f $(BUILDDIR)/-openssl-$(OPENSSL_VER)/build-configured $(BUILDDIR)/-openssl-$(OPENSSL_VER)/build-compiled
diff --git a/deps/tools/common.mk b/deps/tools/common.mk
index 212b576fb63f9..01b57316f9d1a 100644
--- a/deps/tools/common.mk
+++ b/deps/tools/common.mk
@@ -59,6 +59,7 @@ CMAKE_COMMON += -DCMAKE_SYSTEM_NAME=Windows
CMAKE_COMMON += -DCMAKE_RC_COMPILER="$$(which $(CROSS_COMPILE)windres)"
endif
+# For now this is LLVM specific, but I expect it won't be in the future
ifeq ($(CMAKE_GENERATOR),Ninja)
CMAKE_GENERATOR_COMMAND := -G Ninja
else ifeq ($(CMAKE_GENERATOR),make)
@@ -67,27 +68,6 @@ else
$(error Unknown CMake generator '$(CMAKE_GENERATOR)'. Options are 'Ninja' and 'make')
endif
-ifneq (,$(findstring MINGW,$(RAW_BUILD_OS)))
-ifneq (,$(shell ldd $(shell which cmake) | grep msys-2.0.dll))
-# Detect MSYS2 with cygwin CMake rather than MinGW cmake - the former fails to
-# properly drive MinGW tools
-override CMAKE := echo "ERROR: CMake is Cygwin CMake, not MinGW CMake. Build will fail. Use 'pacman -S mingw-w64-{i686,x86_64}-cmake'."; exit 1; $(CMAKE)
-endif
-# In our setup, CMAKE_INSTALL_PREFIX is a relative path inside usr-staging.
-# We do not want this converted to a windows path, because our make system
-# assumes it to be relative to msys `/`.
-override CMAKE := MSYS2_ARG_CONV_EXCL="-DCMAKE_INSTALL_PREFIX" $(CMAKE)
-endif
-
-# Some dependencies' tarballs contains symlinks to non-existent targets. This breaks the
-# the default msys strategy `deepcopy` symlink strategy. To workaround this,
-# switch to `native` which tries native windows symlinks (possible if the
-# machine is in developer mode) - or if not, falls back to cygwin-style
-# symlinks. We don't particularly care either way - we just need to symlinks
-# to succeed. We could guard this by a uname check, but it's harmless elsewhere,
-# so let's not incur the additional overhead.
-MSYS_NONEXISTENT_SYMLINK_TARGET_FIX := export MSYS=winsymlinks:native
-
# If the top-level Makefile is called with environment variables,
# they will override the values passed above to ./configure
MAKE_COMMON := DESTDIR="" prefix=$(build_prefix) bindir=$(build_depsbindir) libdir=$(build_libdir) shlibdir=$(build_shlibdir) libexecdir=$(build_libexecdir) datarootdir=$(build_datarootdir) includedir=$(build_includedir) sysconfdir=$(build_sysconfdir) O=
@@ -164,7 +144,7 @@ upper = $(shell echo $1 | tr a-z A-Z)
# this rule ensures that make install is more nearly atomic
# so it's harder to get half-installed (or half-reinstalled) dependencies
# # and enables sharing deps compiles, uninstall, and fast reinstall
-MAKE_INSTALL = MSYS2_ARG_CONV_EXCL="prefix=" $$(MAKE) -C $1 install $$(MAKE_COMMON) $3 DESTDIR="$2"
+MAKE_INSTALL = +$$(MAKE) -C $1 install $$(MAKE_COMMON) $3 DESTDIR="$2"
define SHLIBFILE_INSTALL
mkdir -p $2/$$(build_shlibdir)
diff --git a/pkgimage.mk b/pkgimage.mk
index 78b2618be549f..e90e0e9618808 100644
--- a/pkgimage.mk
+++ b/pkgimage.mk
@@ -26,7 +26,7 @@ print-depot-path:
$(BUILDDIR)/stdlib/%.image: $(JULIAHOME)/stdlib/Project.toml $(JULIAHOME)/stdlib/Manifest.toml $(INDEPENDENT_STDLIBS_SRCS) $(JULIA_DEPOT_PATH)/compiled
@$(call PRINT_JULIA, JULIA_CPU_TARGET="$(JULIA_CPU_TARGET)" $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no -e \
- 'Base.Precompilation.precompilepkgs(configs=[``=>Base.CacheFlags(debug_level=2, opt_level=3), ``=>Base.CacheFlags(check_bounds=1, debug_level=2, opt_level=3)])')
+ 'Base.Precompilation.precompilepkgs(configs=[``=>Base.CacheFlags(debug_level=2, opt_level=3), ``=>Base.CacheFlags(check_bounds=1, debug_level=2, opt_level=3)]; strict=true)')
touch $@
$(BUILDDIR)/stdlib/release.image: $(build_private_libdir)/sys.$(SHLIB_EXT)
diff --git a/src/Makefile b/src/Makefile
index 6245139592db1..e859acc765354 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -75,7 +75,7 @@ GC_CODEGEN_SRCS += llvm-late-gc-lowering-stock
endif
CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop \
llvm-pass-helpers llvm-ptls \
- llvm-lower-handlers llvm-propagate-addrspaces \
+ llvm-lower-handlers llvm-propagate-addrspaces null_sysimage \
llvm-multiversioning llvm-alloc-opt llvm-alloc-helpers cgmemmgr llvm-remove-addrspaces \
llvm-remove-ni llvm-julia-licm llvm-demote-float16 llvm-cpufeatures pipeline llvm_api \
$(GC_CODEGEN_SRCS)
@@ -192,9 +192,7 @@ endif
COMMON_LIBPATHS := -L$(build_libdir) -L$(build_shlibdir)
RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) $(MMTK_LIB)
-# NB: CG needs uv_mutex_* symbols, but we expect to export them from libjulia-internal
CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) $(MMTK_LIB)
-
RT_DEBUG_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp-debug.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport-debug.a -ljulia-debug $(RT_LIBS)
CG_DEBUG_LIBS := $(COMMON_LIBPATHS) $(CG_LIBS) -ljulia-debug -ljulia-internal-debug
RT_RELEASE_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport.a -ljulia $(RT_LIBS)
@@ -471,10 +469,10 @@ libjulia-codegen-debug: $(build_shlibdir)/libjulia-codegen-debug.$(JL_MAJOR_MINO
libjulia-codegen-debug libjulia-codegen-release: $(PUBLIC_HEADER_TARGETS)
# set the exports for the source files based on where they are getting linked
-$(OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL -DBUILDING_UV_SHARED
-$(DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL -DBUILDING_UV_SHARED
-$(CODEGEN_OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN -DUSING_UV_SHARED
-$(CODEGEN_DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN -DUSING_UV_SHARED
+$(OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL
+$(DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_INTERNAL
+$(CODEGEN_OBJS): SHIPFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN
+$(CODEGEN_DOBJS): DEBUGFLAGS += -DJL_LIBRARY_EXPORTS_CODEGEN
clean:
-rm -fr $(build_shlibdir)/libjulia-internal* $(build_shlibdir)/libjulia-codegen* $(build_shlibdir)/libccalltest* $(build_shlibdir)/libllvmcalltest*
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 8f0644c0cff7f..8859452f3f11d 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -6159,6 +6159,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result)
Value *scope_ptr = get_scope_field(ctx);
jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(
ctx.builder.CreateAlignedStore(scope_to_restore, scope_ptr, ctx.types().alignof_ptr));
+ // NOTE: wb not needed here, due to store to current_task (see jl_gc_wb_current_task)
}
}
else if (head == jl_pop_exception_sym) {
@@ -9334,12 +9335,12 @@ static jl_llvm_functions_t
Value *scope_ptr = get_scope_field(ctx);
LoadInst *current_scope = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, scope_ptr, ctx.types().alignof_ptr);
StoreInst *scope_store = ctx.builder.CreateAlignedStore(scope_boxed, scope_ptr, ctx.types().alignof_ptr);
+ // NOTE: wb not needed here, due to store to current_task (see jl_gc_wb_current_task)
jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(current_scope);
jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe).decorateInst(scope_store);
- // GC preserve the scope, since it is not rooted in the `jl_handler_t *`
- // and may be removed from jl_current_task by any nested block and then
- // replaced later
- Value *scope_token = ctx.builder.CreateCall(prepare_call(gc_preserve_begin_func), {scope_boxed});
+ // GC preserve the current_scope, since it is not rooted in the `jl_handler_t *`,
+ // the newly entered scope is preserved through the current_task.
+ Value *scope_token = ctx.builder.CreateCall(prepare_call(gc_preserve_begin_func), {current_scope});
ctx.scope_restore[cursor] = std::make_pair(scope_token, current_scope);
}
}
diff --git a/src/flisp/Makefile b/src/flisp/Makefile
index 2e902a31dbeed..eca1de86e588a 100644
--- a/src/flisp/Makefile
+++ b/src/flisp/Makefile
@@ -117,7 +117,7 @@ $(BUILDDIR)/host/Makefile:
@printf "%s\n" 'include $(SRCDIR)/Makefile' >> $@
$(BUILDDIR)/host/$(EXENAME): $(BUILDDIR)/host/Makefile | ${BUILDDIR}/host/flisp.boot
- $(MAKE) -C $(BUILDDIR)/host $(EXENAME)
+ make -C $(BUILDDIR)/host $(EXENAME)
$(BUILDDIR)/host/flisp.boot: $(SRCDIR)/flisp.boot | $(BUILDDIR)/host/Makefile
diff --git a/src/gc-interface.h b/src/gc-interface.h
index 826e91355b17a..9c59e274d1261 100644
--- a/src/gc-interface.h
+++ b/src/gc-interface.h
@@ -240,7 +240,14 @@ STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT
// so write barriers can be omitted until the next allocation. This function is a no-op that
// can be used to annotate that a write barrier would be required were it not for this property
// (as opposed to somebody just having forgotten to think about write barriers).
-STATIC_INLINE void jl_gc_wb_fresh(const void *parent, const void *ptr) JL_NOTSAFEPOINT {}
+STATIC_INLINE void jl_gc_wb_fresh(const void *parent JL_UNUSED, const void *ptr JL_UNUSED) JL_NOTSAFEPOINT {}
+// As an optimization, the current_task is explicitly added to the remset while it is running.
+// Upon deschedule, we conservatively move the write barrier into the young generation.
+// This allows the omission of write barriers for all GC roots on the current task stack (JL_GC_PUSH_*),
+// as well as the Task's explicit fields (but only for the current task).
+// This function is a no-op that can be used to annotate that a write barrier would be required were
+// it not for this property (as opposed to somebody just having forgotten to think about write barriers).
+STATIC_INLINE void jl_gc_wb_current_task(const void *parent JL_UNUSED, const void *ptr JL_UNUSED) JL_NOTSAFEPOINT {}
// Used to annotate that a write barrier would be required, but may be omitted because `ptr`
// is known to be an old object.
STATIC_INLINE void jl_gc_wb_knownold(const void *parent, const void *ptr) JL_NOTSAFEPOINT {}
diff --git a/src/gc-pages.c b/src/gc-pages.c
index 71d59de29166f..79dd8993a8861 100644
--- a/src/gc-pages.c
+++ b/src/gc-pages.c
@@ -28,6 +28,28 @@ JL_DLLEXPORT uint64_t jl_get_pg_size(void)
static int block_pg_cnt = DEFAULT_BLOCK_PG_ALLOC;
+// Julia allocates large blocks (64M) with mmap. These are never
+// unmapped but the underlying physical memory may be released
+// with calls to madvise(MADV_DONTNEED).
+static uint64_t poolmem_blocks_allocated_total = 0;
+
+JL_DLLEXPORT uint64_t jl_poolmem_blocks_allocated_total(void)
+{
+ return poolmem_blocks_allocated_total;
+}
+
+JL_DLLEXPORT uint64_t jl_poolmem_bytes_allocated(void)
+{
+ return jl_atomic_load_relaxed(&gc_heap_stats.bytes_resident);
+}
+
+JL_DLLEXPORT uint64_t jl_current_pg_count(void)
+{
+ assert(jl_page_size == GC_PAGE_SZ && "RAI fork of Julia should be running on platforms for which jl_page_size == GC_PAGE_SZ");
+ size_t nb = jl_atomic_load_relaxed(&gc_heap_stats.bytes_resident);
+ return nb / GC_PAGE_SZ; // exact division
+}
+
void jl_gc_init_page(void)
{
if (GC_PAGE_SZ * block_pg_cnt < jl_page_size)
@@ -55,6 +77,11 @@ char *jl_gc_try_alloc_pages_(int pg_cnt) JL_NOTSAFEPOINT
MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED)
return NULL;
+
+#ifdef MADV_NOHUGEPAGE
+ madvise(mem, pages_sz, MADV_NOHUGEPAGE);
+#endif
+
#endif
if (GC_PAGE_SZ > jl_page_size)
// round data pointer up to the nearest gc_page_data-aligned
@@ -62,6 +89,7 @@ char *jl_gc_try_alloc_pages_(int pg_cnt) JL_NOTSAFEPOINT
mem = (char*)gc_page_data(mem + GC_PAGE_SZ - 1);
jl_atomic_fetch_add_relaxed(&gc_heap_stats.bytes_mapped, pages_sz);
jl_atomic_fetch_add_relaxed(&gc_heap_stats.bytes_resident, pages_sz);
+ poolmem_blocks_allocated_total++; // RAI-specific
return mem;
}
@@ -184,7 +212,7 @@ void jl_gc_free_page(jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT
}
#ifdef _OS_WINDOWS_
VirtualFree(p, decommit_size, MEM_DECOMMIT);
-#elif defined(MADV_FREE)
+#elif 0
static int supports_madv_free = 1;
if (supports_madv_free) {
if (madvise(p, decommit_size, MADV_FREE) == -1) {
diff --git a/src/gc-stacks.c b/src/gc-stacks.c
index 9387c7fb065ec..55c4a470dd33e 100644
--- a/src/gc-stacks.c
+++ b/src/gc-stacks.c
@@ -72,8 +72,10 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT
munmap(stk, bufsz);
return MAP_FAILED;
}
-# endif
-
+#ifdef MADV_NOHUGEPAGE
+ madvise(stk, bufsz, MADV_NOHUGEPAGE);
+#endif
+#endif
jl_atomic_fetch_add_relaxed(&num_stack_mappings, 1);
return stk;
}
diff --git a/src/gc-stock.c b/src/gc-stock.c
index 55f31b26679ff..0d15da602caf0 100644
--- a/src/gc-stock.c
+++ b/src/gc-stock.c
@@ -3397,6 +3397,9 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection)
return recollect;
}
+extern int jl_heartbeat_pause(void);
+extern int jl_heartbeat_resume(void);
+
JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
{
JL_PROBE_GC_BEGIN(collection);
@@ -3439,6 +3442,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
// existence of the thread in the jl_n_threads count.
//
// TODO: concurrently queue objects
+ jl_heartbeat_pause();
jl_fence();
gc_n_threads = jl_atomic_load_acquire(&jl_n_threads);
gc_all_tls_states = jl_atomic_load_relaxed(&jl_all_tls_states);
@@ -3470,6 +3474,7 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
gc_n_threads = 0;
gc_all_tls_states = NULL;
+ jl_heartbeat_resume();
jl_safepoint_end_gc();
jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING);
JL_PROBE_GC_END();
@@ -3906,6 +3911,9 @@ void *jl_gc_perm_alloc_nolock(size_t sz, int zero, unsigned align, unsigned offs
errno = last_errno;
if (__unlikely(pool == MAP_FAILED))
return NULL;
+#ifdef MADV_NOHUGEPAGE
+ madvise(pool, GC_PERM_POOL_SIZE, MADV_NOHUGEPAGE);
+#endif
#endif
gc_perm_pool = (uintptr_t)pool;
gc_perm_end = gc_perm_pool + GC_PERM_POOL_SIZE;
diff --git a/src/gc-stock.h b/src/gc-stock.h
index d478ee1366da0..41e6151605d80 100644
--- a/src/gc-stock.h
+++ b/src/gc-stock.h
@@ -499,6 +499,9 @@ extern uv_sem_t gc_sweep_assists_needed;
extern _Atomic(int) gc_n_threads_marking;
extern _Atomic(int) gc_n_threads_sweeping_pools;
extern _Atomic(int) n_threads_running;
+extern _Atomic(int) gc_n_threads_sweeping_stacks;
+extern _Atomic(int) gc_ptls_sweep_idx;
+extern _Atomic(int) gc_stack_free_idx;
extern uv_barrier_t thread_init_done;
void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_markqueue_t *mq);
void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t *fl_parent, jl_value_t **fl_begin, jl_value_t **fl_end) JL_NOTSAFEPOINT;
diff --git a/src/gf.c b/src/gf.c
index 47db5474701ac..59604d67c584b 100644
--- a/src/gf.c
+++ b/src/gf.c
@@ -3890,6 +3890,15 @@ JL_DLLEXPORT void jl_compile_method_sig(jl_method_t *m, jl_value_t *types, jl_sv
jl_compile_method_instance(mi, NULL, world);
}
+JL_DLLEXPORT int jl_is_compilable(jl_tupletype_t *types)
+{
+ size_t world = jl_atomic_load_acquire(&jl_world_counter);
+ size_t min_valid = 0;
+ size_t max_valid = ~(size_t)0;
+ jl_method_instance_t *mi = jl_get_compile_hint_specialization(types, world, &min_valid, &max_valid, 1);
+ return mi == NULL ? 0 : 1;
+}
+
JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types)
{
size_t world = jl_atomic_load_acquire(&jl_world_counter);
diff --git a/src/init.c b/src/init.c
index e11b360b5d378..a0d2719230e77 100644
--- a/src/init.c
+++ b/src/init.c
@@ -553,6 +553,8 @@ extern jl_mutex_t newly_inferred_mutex;
extern jl_mutex_t global_roots_lock;
extern jl_mutex_t profile_show_peek_cond_lock;
+extern void jl_init_heartbeat(void);
+
static void restore_fp_env(void)
{
if (jl_set_zero_subnormals(0) || jl_set_default_nans(0)) {
@@ -612,6 +614,11 @@ static NOINLINE void _finish_jl_init_(jl_image_buf_t sysimage, jl_ptls_t ptls, j
jl_start_gc_threads();
uv_barrier_wait(&thread_init_done);
+ if (jl_base_module != NULL) {
+ // requires code in Base
+ jl_init_heartbeat();
+ }
+
jl_gc_enable(1);
if ((sysimage.kind != JL_IMAGE_KIND_NONE) &&
@@ -750,6 +757,15 @@ JL_DLLEXPORT void jl_init_(jl_image_buf_t sysimage)
if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
jl_install_default_signal_handlers();
+#if (defined(_OS_LINUX_) && defined(_CPU_X86_64_)) || (defined(_OS_DARWIN_) && defined(_CPU_AARCH64_))
+ if (jl_options.safe_crash_log_file != NULL) {
+ jl_sig_fd = open(jl_options.safe_crash_log_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
+ if (jl_sig_fd == -1) {
+ jl_error("fatal error: could not open safe crash log file for writing");
+ }
+ }
+#endif
+
jl_gc_init();
arraylist_new(&jl_linkage_blobs, 0);
diff --git a/src/interpreter.c b/src/interpreter.c
index a692cadaf9be1..55f8ec9188b19 100644
--- a/src/interpreter.c
+++ b/src/interpreter.c
@@ -539,12 +539,12 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
}
s->locals[jl_source_nslots(s->src) + ip] = jl_box_ulong(jl_excstack_state(ct));
if (jl_enternode_scope(stmt)) {
- jl_value_t *scope = eval_value(jl_enternode_scope(stmt), s);
- // GC preserve the scope, since it is not rooted in the `jl_handler_t *`
- // and may be removed from jl_current_task by any nested block and then
- // replaced later
- JL_GC_PUSH1(&scope);
- ct->scope = scope;
+ jl_value_t *old_scope = ct->scope; // Identical to __eh.scope
+ // GC preserve the old_scope, since it is not rooted in the `jl_handler_t *`,
+ // the newly entered scope is preserved through the current_task.
+ JL_GC_PUSH1(&old_scope);
+ ct->scope = eval_value(jl_enternode_scope(stmt), s);
+ jl_gc_wb_current_task(ct, ct->scope);
if (!jl_setjmp(__eh.eh_ctx, 0)) {
ct->eh = &__eh;
eval_body(stmts, s, next_ip, toplevel);
diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc
index 61420c7306de9..1a74581745972 100644
--- a/src/jl_exported_funcs.inc
+++ b/src/jl_exported_funcs.inc
@@ -271,6 +271,7 @@
XX(jl_istopmod) \
XX(jl_is_binding_deprecated) \
XX(jl_is_char_signed) \
+ XX(jl_is_compilable) \
XX(jl_is_const) \
XX(jl_is_assertsbuild) \
XX(jl_is_debugbuild) \
diff --git a/src/jl_uv.c b/src/jl_uv.c
index a21b05433b8c6..5d8ba54c06c4e 100644
--- a/src/jl_uv.c
+++ b/src/jl_uv.c
@@ -15,6 +15,7 @@
#include "errno.h"
#include
#include
+#include
#endif
#include "julia.h"
@@ -813,6 +814,83 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...)
return c;
}
+STATIC_INLINE int copystp(char *dest, const char *src)
+{
+ char *d = stpcpy(dest, src);
+ return (int)(d - dest);
+}
+
+// RAI-specific
+STATIC_INLINE void write_to_safe_crash_log(char *buf) JL_NOTSAFEPOINT
+{
+ int buflen = strlen(buf);
+ // Our telemetry on SPCS expects a JSON object per line.
+ // We ignore write failures because there is nothing we can do.
+ // We'll use a 2K byte buffer: 69 bytes for JSON message decorations,
+ // 1 byte for the terminating NUL character, and 3 bytes for an
+ // ellipsis if we have to truncate the message leaves `max_b` bytes
+ // for the message.
+ const int wbuflen = 2048;
+ const int max_b = wbuflen - 70 - 3;
+ char wbuf[wbuflen];
+ bzero(wbuf, wbuflen);
+ int wlen = 0;
+
+ // JSON preamble (32 bytes)
+ wlen += copystp(&wbuf[wlen], "\n{\"level\":\"Error\", \"timestamp\":\"");
+
+ // Timestamp (19 bytes)
+ struct timeval tv;
+ struct tm* tm_info;
+ gettimeofday(&tv, NULL);
+ tm_info = gmtime(&tv.tv_sec);
+ wlen += strftime(&wbuf[wlen], 42, "%Y-%m-%dT%H:%M:%S", tm_info);
+ sprintf(&wbuf[wlen], ".%03ld", (long)tv.tv_usec / 1000);
+ wlen += 4;
+
+ // JSON preamble to message (15 bytes)
+ wlen += copystp(&wbuf[wlen], "\", \"message\": \"");
+
+ // Message
+ // Each iteration will advance wlen by 1 or 2
+ for (size_t i = 0; i < buflen; i++) {
+ // Truncate the message if the write buffer is full
+ if (wlen == max_b || wlen == max_b - 1) {
+ wlen += copystp(&wbuf[wlen], "...");
+ break;
+ }
+ switch (buf[i]) {
+ case '"':
+ wlen += copystp(&wbuf[wlen], "\\\"");
+ break;
+ case '\b':
+ wlen += copystp(&wbuf[wlen], "\\b");
+ break;
+ case '\n':
+ wlen += copystp(&wbuf[wlen], "\\n");
+ break;
+ case '\r':
+ wlen += copystp(&wbuf[wlen], "\\r");
+ break;
+ case '\t':
+ wlen += copystp(&wbuf[wlen], "\\t");
+ break;
+ case '\\':
+ wlen += copystp(&wbuf[wlen], "\\\\");
+ break;
+ default:
+ wbuf[wlen++] = buf[i];
+ break;
+ }
+ }
+ // JSON completion (3 bytes)
+ wlen += copystp(&wbuf[wlen], "\"}\n");
+ write(jl_sig_fd, wbuf, wlen);
+ fdatasync(jl_sig_fd);
+}
+
+extern int jl_inside_heartbeat_thread(void);
+
JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
{
static char buf[1000];
@@ -829,6 +907,12 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
va_end(args);
buf[999] = '\0';
+ // order is important here: we want to ensure that the threading infra
+ // has been initialized before we start trying to print to the
+ // safe crash log file
+ if (jl_sig_fd != 0 && (jl_inside_signal_handler() || jl_inside_heartbeat_thread())) {
+ write_to_safe_crash_log(buf);
+ }
if (write(STDERR_FILENO, buf, strlen(buf)) < 0) {
// nothing we can do; ignore the failure
}
diff --git a/src/jloptions.c b/src/jloptions.c
index 96cce1c8d29a3..e128045b5b5af 100644
--- a/src/jloptions.c
+++ b/src/jloptions.c
@@ -155,6 +155,7 @@ JL_DLLEXPORT void jl_init_options(void)
JL_TRIM_NO, // trim
0, // task_metrics
-1, // timeout_for_safepoint_straggler_s
+ NULL, // safe_crash_log_file
};
jl_options_initialized = 1;
}
@@ -384,6 +385,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
opt_permalloc_pkgimg,
opt_trim,
opt_experimental_features,
+ opt_safe_crash_log_file,
};
static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:m:";
static const struct option longopts[] = {
@@ -452,6 +454,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
{ "permalloc-pkgimg",required_argument, 0, opt_permalloc_pkgimg },
{ "heap-size-hint", required_argument, 0, opt_heap_size_hint },
{ "trim", optional_argument, 0, opt_trim },
+ { "safe-crash-log-file", required_argument, 0, opt_safe_crash_log_file },
{ 0, 0, 0, 0 }
};
@@ -668,9 +671,11 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
}
jl_options.nthreads = nthreads + nthreadsi;
}
+ assert(jl_options.nthreadpools == 1 || jl_options.nthreadpools == 2);
int16_t *ntpp = (int16_t *)malloc_s(jl_options.nthreadpools * sizeof(int16_t));
ntpp[0] = (int16_t)nthreads;
- ntpp[1] = (int16_t)nthreadsi;
+ if (jl_options.nthreadpools == 2)
+ ntpp[1] = (int16_t)nthreadsi;
jl_options.nthreads_per_pool = ntpp;
break;
case 'p': // procs
@@ -1011,6 +1016,10 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
jl_options.task_metrics = JL_OPTIONS_TASK_METRICS_ON;
else
jl_errorf("julia: invalid argument to --task-metrics={yes|no} (%s)", optarg);
+ case opt_safe_crash_log_file:
+ jl_options.safe_crash_log_file = strdup(optarg);
+ if (jl_options.safe_crash_log_file == NULL)
+ jl_error("julia: failed to allocate memory for --safe-crash-log-file");
break;
default:
jl_errorf("julia: unhandled option -- %c\n"
diff --git a/src/jloptions.h b/src/jloptions.h
index 06e00e9309dba..f5c8f72a2cb6c 100644
--- a/src/jloptions.h
+++ b/src/jloptions.h
@@ -68,6 +68,7 @@ typedef struct {
int8_t trim;
int8_t task_metrics;
int16_t timeout_for_safepoint_straggler_s;
+ const char *safe_crash_log_file;
} jl_options_t;
#endif
diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm
index aad8105b34308..07668878a20e1 100644
--- a/src/julia-syntax.scm
+++ b/src/julia-syntax.scm
@@ -1790,11 +1790,13 @@
(cons e '())
(let ((a '()))
(define (arg-to-temp x)
- (cond ((effect-free? x) x)
- ((or (eq? (car x) '...) (eq? (car x) '&))
- `(,(car x) ,(arg-to-temp (cadr x))))
+ (cond ((effect-free? x) x)
+ ((eq? (car x) '...)
+ `(... ,(arg-to-temp (cadr x))))
((eq? (car x) 'kw)
- `(,(car x) ,(cadr x) ,(arg-to-temp (caddr x))))
+ `(kw ,(cadr x) ,(arg-to-temp (caddr x))))
+ ((eq? (car x) 'parameters)
+ `(parameters ,@(map arg-to-temp (cdr x))))
(else
(let ((g (make-ssavalue)))
(begin (set! a (cons `(= ,g ,x) a))
diff --git a/src/julia_internal.h b/src/julia_internal.h
index c9e1b0e204df6..52832b32b25e0 100644
--- a/src/julia_internal.h
+++ b/src/julia_internal.h
@@ -230,6 +230,7 @@ extern volatile size_t profile_bt_size_max;
extern volatile size_t profile_bt_size_cur;
extern volatile int profile_running;
extern volatile int profile_all_tasks;
+extern int heartbeat_tid; // Mostly used to ensure we skip this thread in the CPU profiler. XXX: not implemented on Windows
// Ensures that we can safely read the `live_tasks`field of every TLS when profiling.
// We want to avoid the case that a GC gets interleaved with `jl_profile_task` and shrinks
// the `live_tasks` array while we are reading it or frees tasks that are being profiled.
@@ -245,6 +246,7 @@ extern uv_mutex_t bt_data_prof_lock;
#define PROFILE_STATE_THREAD_NOT_SLEEPING (1)
#define PROFILE_STATE_THREAD_SLEEPING (2)
#define PROFILE_STATE_WALL_TIME_PROFILING (3)
+extern _Atomic(int) n_threads_running;
void jl_profile_task(void);
// number of cycles since power-on
@@ -780,6 +782,32 @@ JL_CALLABLE(jl_f_tuple);
void jl_install_default_signal_handlers(void);
void restore_signals(void);
void jl_install_thread_signal_handler(jl_ptls_t ptls);
+extern const size_t sig_stack_size;
+STATIC_INLINE int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr)
+{
+ // One guard page for signal_stack.
+ return !((char*)ptr < (char*)ptls->signal_stack - jl_page_size ||
+ (char*)ptr > (char*)ptls->signal_stack + sig_stack_size);
+}
+STATIC_INLINE int jl_inside_signal_handler(void)
+{
+#if (defined(_OS_LINUX_) && defined(_CPU_X86_64_)) || (defined(_OS_DARWIN_) && defined(_CPU_AARCH64_))
+ // Read the stack pointer
+ size_t sp;
+#if defined(_OS_LINUX_) && defined(_CPU_X86_64_)
+ __asm__ __volatile__("movq %%rsp, %0" : "=r"(sp));
+#elif defined(_OS_DARWIN_) && defined(_CPU_AARCH64_)
+ __asm__ __volatile__("mov %0, sp" : "=r"(sp));
+#endif
+ // Check if the stack pointer is within the signal stack
+ jl_ptls_t ptls = jl_current_task->ptls;
+ return is_addr_on_sigstack(ptls, (void*)sp);
+#else
+ return 0;
+#endif
+}
+// File-descriptor for safe logging on signal handling
+extern int jl_sig_fd;
extern uv_loop_t *jl_io_loop;
JL_DLLEXPORT void jl_uv_flush(uv_stream_t *stream);
@@ -1499,9 +1527,9 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip);
void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *ct);
JL_DLLEXPORT void jl_raise_debugger(void) JL_NOTSAFEPOINT;
JL_DLLEXPORT void jl_gdblookup(void* ip) JL_NOTSAFEPOINT;
-void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT;
-void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_data) JL_NOTSAFEPOINT;
-JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT;
+void jl_print_native_codeloc(char *pre_str, uintptr_t ip) JL_NOTSAFEPOINT;
+void jl_print_bt_entry_codeloc(int sig, jl_bt_element_t *bt_data) JL_NOTSAFEPOINT;
+JL_DLLEXPORT void jl_print_task_backtraces(int show_done)JL_NOTSAFEPOINT ;
#ifdef _OS_WINDOWS_
JL_DLLEXPORT void jl_refresh_dbg_module_list(void);
#endif
diff --git a/src/options.h b/src/options.h
index 0715069faab32..fb2797ffd0336 100644
--- a/src/options.h
+++ b/src/options.h
@@ -144,6 +144,9 @@
#define MACHINE_EXCLUSIVE_NAME "JULIA_EXCLUSIVE"
#define DEFAULT_MACHINE_EXCLUSIVE 0
+// heartbeats
+#define JL_HEARTBEAT_THREAD
+
// sanitizer defaults ---------------------------------------------------------
// Automatically enable MEMDEBUG and KEEP_BODIES for the sanitizers
diff --git a/src/rtutils.c b/src/rtutils.c
index 6d4a375017bf2..77ce3c0d4adb0 100644
--- a/src/rtutils.c
+++ b/src/rtutils.c
@@ -296,6 +296,7 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_task_t *ct, jl_handler_t *eh)
ct->eh = eh->prev;
ct->gcstack = eh->gcstack;
ct->scope = eh->scope;
+ jl_gc_wb_current_task(ct, ct->scope);
small_arraylist_t *locks = &ptls->locks;
int unlocks = locks->len > eh->locks_len;
if (unlocks) {
@@ -335,6 +336,7 @@ JL_DLLEXPORT void jl_eh_restore_state_noexcept(jl_task_t *ct, jl_handler_t *eh)
{
assert(ct->gcstack == eh->gcstack && "Incorrect GC usage under try catch");
ct->scope = eh->scope;
+ jl_gc_wb_current_task(ct, ct->scope);
ct->eh = eh->prev;
ct->ptls->defer_signal = eh->defer_signal; // optional, but certain try-finally (in stream.jl) may be slightly harder to write without this
}
diff --git a/src/safepoint.c b/src/safepoint.c
index 970c48875d790..96da3c1a05eb1 100644
--- a/src/safepoint.c
+++ b/src/safepoint.c
@@ -172,7 +172,7 @@ void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads)
size_t bt_size = jl_try_record_thread_backtrace(ptls2, ptls->bt_data, JL_MAX_BT_SIZE);
// Print the backtrace of the straggler
for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(ptls->bt_data + i)) {
- jl_print_bt_entry_codeloc(ptls->bt_data + i);
+ jl_print_bt_entry_codeloc(-1, ptls->bt_data + i);
}
}
}
diff --git a/src/signal-handling.c b/src/signal-handling.c
index b03f0c1a430cd..269885bb5c14c 100644
--- a/src/signal-handling.c
+++ b/src/signal-handling.c
@@ -30,6 +30,8 @@ static const uint64_t GIGA = 1000000000ULL;
// Timers to take samples at intervals
JL_DLLEXPORT void jl_profile_stop_timer(void);
JL_DLLEXPORT int jl_profile_start_timer(uint8_t);
+// File-descriptor for safe logging on signal handling
+int jl_sig_fd;
///////////////////////
// Utility functions //
@@ -647,7 +649,7 @@ void jl_critical_error(int sig, int si_code, bt_context_t *context, jl_task_t *c
*bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context, NULL);
}
for (i = 0; i < n; i += jl_bt_entry_size(bt_data + i)) {
- jl_print_bt_entry_codeloc(bt_data + i);
+ jl_print_bt_entry_codeloc(sig, bt_data + i);
}
jl_gc_debug_print_status();
jl_gc_debug_critical_error();
diff --git a/src/signals-mach.c b/src/signals-mach.c
index 4a670547bdfcd..12d545ee81ce6 100644
--- a/src/signals-mach.c
+++ b/src/signals-mach.c
@@ -824,6 +824,10 @@ void *mach_profile_listener(void *arg)
for (int idx = nthreads; idx-- > 0; ) {
// Stop the threads in random order.
int i = randperm[idx];
+ // skip heartbeat thread
+ if (i == heartbeat_tid) {
+ continue;
+ }
jl_profile_thread_mach(i);
}
}
diff --git a/src/signals-unix.c b/src/signals-unix.c
index 2db397050420e..ed186d5b357cd 100644
--- a/src/signals-unix.c
+++ b/src/signals-unix.c
@@ -42,7 +42,7 @@
#endif
// 8M signal stack, same as default stack size (though we barely use this)
-static const size_t sig_stack_size = 8 * 1024 * 1024;
+const size_t sig_stack_size = 8 * 1024 * 1024;
#include "julia_assert.h"
@@ -102,14 +102,6 @@ static inline uintptr_t jl_get_rsp_from_ctx(const void *_ctx)
#endif
}
-static int is_addr_on_sigstack(jl_ptls_t ptls, void *ptr) JL_NOTSAFEPOINT
-{
- // One guard page for signal_stack.
- return ptls->signal_stack == NULL ||
- ((char*)ptr >= (char*)ptls->signal_stack - jl_page_size &&
- (char*)ptr <= (char*)ptls->signal_stack + (ptls->signal_stack_size ? ptls->signal_stack_size : sig_stack_size));
-}
-
// Modify signal context `_ctx` so that `fptr` will execute when the signal returns
// The function `fptr` itself must not return.
JL_NO_ASAN static void jl_call_in_ctx(jl_ptls_t ptls, void (*fptr)(void), int sig, void *_ctx)
@@ -873,6 +865,10 @@ static void do_profile(void *ctx)
for (int idx = nthreads; idx-- > 0; ) {
// Stop the threads in the random order.
int tid = randperm[idx];
+ // skip heartbeat thread
+ if (tid == heartbeat_tid) {
+ return;
+ }
// do backtrace for profiler
if (!profile_running)
return;
@@ -1111,7 +1107,7 @@ static void *signal_listener(void *arg)
jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig));
size_t i;
for (i = 0; i < signal_bt_size; i += jl_bt_entry_size(signal_bt_data + i)) {
- jl_print_bt_entry_codeloc(signal_bt_data + i);
+ jl_print_bt_entry_codeloc(sig, signal_bt_data + i);
}
}
}
diff --git a/src/signals-win.c b/src/signals-win.c
index c8ae74f52dba4..d9c7ffd5ae769 100644
--- a/src/signals-win.c
+++ b/src/signals-win.c
@@ -4,7 +4,7 @@
// Note that this file is `#include`d by "signal-handling.c"
#include // hidden by LEAN_AND_MEAN
-static const size_t sig_stack_size = 131072; // 128k reserved for backtrace_fiber for stack overflow handling
+const size_t sig_stack_size = 131072; // 128k reserved for backtrace_fiber for stack overflow handling
// Copied from MINGW_FLOAT_H which may not be found due to a collision with the builtin gcc float.h
// eventually we can probably integrate this into OpenLibm.
@@ -333,7 +333,7 @@ LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo)
jl_safe_printf("UNKNOWN"); break;
}
jl_safe_printf(" at 0x%zx -- ", (size_t)ExceptionInfo->ExceptionRecord->ExceptionAddress);
- jl_print_native_codeloc((uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress);
+ jl_print_native_codeloc("", (uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress);
jl_critical_error(0, 0, ExceptionInfo->ContextRecord, ct);
static int recursion = 0;
diff --git a/src/stackwalk.c b/src/stackwalk.c
index 50566b46ff45a..b04142f7cf845 100644
--- a/src/stackwalk.c
+++ b/src/stackwalk.c
@@ -637,22 +637,25 @@ JL_DLLEXPORT jl_value_t *jl_lookup_code_address(void *ip, int skipC)
return rs;
}
-static void jl_safe_print_codeloc(const char* func_name, const char* file_name,
+static void jl_safe_print_codeloc(const char *pre_str,
+ const char* func_name, const char* file_name,
int line, int inlined) JL_NOTSAFEPOINT
{
const char *inlined_str = inlined ? " [inlined]" : "";
if (line != -1) {
- jl_safe_printf("%s at %s:%d%s\n", func_name, file_name, line, inlined_str);
+ jl_safe_printf("%s%s at %s:%d%s\n",
+ pre_str, func_name, file_name, line, inlined_str);
}
else {
- jl_safe_printf("%s at %s (unknown line)%s\n", func_name, file_name, inlined_str);
+ jl_safe_printf("%s%s at %s (unknown line)%s\n",
+ pre_str, func_name, file_name, inlined_str);
}
}
// Print function, file and line containing native instruction pointer `ip` by
// looking up debug info. Prints multiple such frames when `ip` points to
// inlined code.
-void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT
+void jl_print_native_codeloc(char *pre_str, uintptr_t ip) JL_NOTSAFEPOINT
{
// This function is not allowed to reference any TLS variables since
// it can be called from an unmanaged thread on OSX.
@@ -664,10 +667,11 @@ void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT
for (i = 0; i < n; i++) {
jl_frame_t frame = frames[i];
if (!frame.func_name) {
- jl_safe_printf("unknown function (ip: %p) at %s\n", (void*)ip, frame.file_name ? frame.file_name : "(unknown file)");
+ jl_safe_printf("%sunknown function (ip: %p) at %s\n", pre_str, (void*)ip, frame.file_name ? frame.file_name : "(unknown file)");
}
else {
- jl_safe_print_codeloc(frame.func_name, frame.file_name, frame.line, frame.inlined);
+ jl_safe_print_codeloc(pre_str, frame.func_name,
+ frame.file_name, frame.line, frame.inlined);
free(frame.func_name);
}
free(frame.file_name);
@@ -725,7 +729,7 @@ const char *jl_debuginfo_name(jl_value_t *func)
// func == module : top-level
// func == NULL : macro expansion
-static void jl_print_debugloc(jl_debuginfo_t *debuginfo, jl_value_t *func, size_t ip, int inlined) JL_NOTSAFEPOINT
+static void jl_print_debugloc(const char *pre_str, jl_debuginfo_t *debuginfo, jl_value_t *func, size_t ip, int inlined) JL_NOTSAFEPOINT
{
if (!jl_is_symbol(debuginfo->def)) // this is a path or
func = debuginfo->def; // this is inlined code
@@ -734,26 +738,36 @@ static void jl_print_debugloc(jl_debuginfo_t *debuginfo, jl_value_t *func, size_
if (edges_idx) {
jl_debuginfo_t *edge = (jl_debuginfo_t*)jl_svecref(debuginfo->edges, edges_idx - 1);
assert(jl_typetagis(edge, jl_debuginfo_type));
- jl_print_debugloc(edge, NULL, stmt.pc, 1);
+ jl_print_debugloc(pre_str, edge, NULL, stmt.pc, 1);
}
intptr_t ip2 = stmt.line;
if (ip2 >= 0 && ip > 0 && (jl_value_t*)debuginfo->linetable != jl_nothing) {
- jl_print_debugloc(debuginfo->linetable, func, ip2, 0);
+ jl_print_debugloc(pre_str, debuginfo->linetable, func, ip2, 0);
}
else {
if (ip2 < 0) // set broken debug info to ignored
ip2 = 0;
const char *func_name = jl_debuginfo_name(func);
const char *file = jl_debuginfo_file(debuginfo);
- jl_safe_print_codeloc(func_name, file, ip2, inlined);
+ jl_safe_print_codeloc(pre_str, func_name, file, ip2, inlined);
}
}
// Print code location for backtrace buffer entry at *bt_entry
-void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT
+void jl_print_bt_entry_codeloc(int sig, jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT
{
+ char sig_str[32], pre_str[64];
+ sig_str[0] = pre_str[0] = '\0';
+ if (sig != -1) {
+ snprintf(sig_str, 32, "signal (%d) ", sig);
+ }
+ // do not call jl_threadid if there's no current task
+ if (jl_get_current_task()) {
+ snprintf(pre_str, 64, "%sthread (%d) ", sig_str, jl_threadid() + 1);
+ }
+
if (jl_bt_is_native(bt_entry)) {
- jl_print_native_codeloc(bt_entry[0].uintptr);
+ jl_print_native_codeloc(pre_str, bt_entry[0].uintptr);
}
else if (jl_bt_entry_tag(bt_entry) == JL_BT_INTERP_FRAME_TAG) {
size_t ip = jl_bt_entry_header(bt_entry); // zero-indexed
@@ -772,7 +786,7 @@ void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT
if (jl_is_code_info(code)) {
jl_code_info_t *src = (jl_code_info_t*)code;
// See also the debug info handling in codegen.cpp.
- jl_print_debugloc(src->debuginfo, def, ip + 1, 0);
+ jl_print_debugloc(pre_str, src->debuginfo, def, ip + 1, 0);
}
else {
// If we're using this function something bad has already happened;
@@ -1361,7 +1375,13 @@ JL_DLLEXPORT jl_record_backtrace_result_t jl_record_backtrace(jl_task_t *t, jl_b
JL_DLLEXPORT void jl_gdblookup(void* ip)
{
- jl_print_native_codeloc((uintptr_t)ip);
+ char pre_str[64];
+ pre_str[0] = '\0';
+ // do not call jl_threadid if there's no current task
+ if (jl_get_current_task()) {
+ snprintf(pre_str, 64, "thread (%d) ", jl_threadid() + 1);
+ }
+ jl_print_native_codeloc(pre_str, (uintptr_t)ip);
}
// Print backtrace for current exception in catch block
@@ -1376,7 +1396,7 @@ JL_DLLEXPORT void jlbacktrace(void) JL_NOTSAFEPOINT
size_t i, bt_size = jl_excstack_bt_size(s, s->top);
jl_bt_element_t *bt_data = jl_excstack_bt_data(s, s->top);
for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) {
- jl_print_bt_entry_codeloc(bt_data + i);
+ jl_print_bt_entry_codeloc(-1, bt_data + i);
}
}
@@ -1399,7 +1419,7 @@ JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT
size_t bt_size = r.bt_size;
size_t i;
for (i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) {
- jl_print_bt_entry_codeloc(bt_data + i);
+ jl_print_bt_entry_codeloc(-1, bt_data + i);
}
if (bt_size == 0)
jl_safe_printf(" no backtrace recorded\n");
@@ -1410,11 +1430,30 @@ JL_DLLEXPORT void jl_print_backtrace(void) JL_NOTSAFEPOINT
jlbacktrace();
}
-// Print backtraces for all live tasks, for all threads, to jl_safe_printf stderr
+extern int jl_inside_heartbeat_thread(void);
+extern int jl_heartbeat_pause(void);
+extern int jl_heartbeat_resume(void);
+
+// Print backtraces for all live tasks, for all threads, to jl_safe_printf
+// stderr. This can take a _long_ time!
JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT
{
+ // disable heartbeats to prevent heartbeat loss while running this,
+ // unless this is called from the heartbeat thread itself; in that
+ // situation, the thread is busy running this and it will not be
+ // updating the missed heartbeats counter
+ if (!jl_inside_heartbeat_thread()) {
+ jl_heartbeat_pause();
+ }
+
size_t nthreads = jl_atomic_load_acquire(&jl_n_threads);
jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states);
+ int ctid = -1;
+ // do not call jl_threadid if there's no current task
+ if (jl_get_current_task()) {
+ ctid = jl_threadid() + 1;
+ }
+ jl_safe_printf("thread (%d) ++++ Task backtraces\n", ctid);
for (size_t i = 0; i < nthreads; i++) {
jl_ptls_t ptls2 = allstates[i];
if (gc_is_collector_thread(i)) {
@@ -1430,17 +1469,22 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT
jl_task_t *t = ptls2->root_task;
if (t != NULL)
t_state = jl_atomic_load_relaxed(&t->_state);
- jl_safe_printf("==== Thread %d created %zu live tasks\n",
- ptls2->tid + 1, n + (t_state != JL_TASK_STATE_DONE));
+ jl_safe_printf("thread (%d) ==== Thread %d created %zu live tasks\n",
+ ctid, ptls2->tid + 1, n + (t_state != JL_TASK_STATE_DONE));
if (show_done || t_state != JL_TASK_STATE_DONE) {
- jl_safe_printf(" ---- Root task (%p)\n", ptls2->root_task);
+ jl_safe_printf("thread (%d) ---- Root task (%p)\n", ctid, ptls2->root_task);
if (t != NULL) {
- jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n",
- t->sticky, t->ctx.started, t_state,
+ jl_safe_printf("thread (%d) (sticky: %d, started: %d, state: %d, tid: %d)\n",
+ ctid, t->sticky, t->ctx.started, t_state,
jl_atomic_load_relaxed(&t->tid) + 1);
- jlbacktracet(t);
+ if (t->ctx.stkbuf != NULL) {
+ jlbacktracet(t);
+ }
+ else {
+ jl_safe_printf("thread (%d) no stack\n", ctid);
+ }
}
- jl_safe_printf(" ---- End root task\n");
+ jl_safe_printf("thread (%d) ---- End root task\n", ctid);
}
for (size_t j = 0; j < n; j++) {
@@ -1450,17 +1494,24 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT
int t_state = jl_atomic_load_relaxed(&t->_state);
if (!show_done && t_state == JL_TASK_STATE_DONE)
continue;
- jl_safe_printf(" ---- Task %zu (%p)\n", j + 1, t);
+ jl_safe_printf("thread (%d) ---- Task %zu (%p)\n", ctid, j + 1, t);
// n.b. this information might not be consistent with the stack printing after it, since it could start running or change tid, etc.
- jl_safe_printf(" (sticky: %d, started: %d, state: %d, tid: %d)\n",
- t->sticky, t->ctx.started, t_state,
+ jl_safe_printf("thread (%d) (sticky: %d, started: %d, state: %d, tid: %d)\n",
+ ctid, t->sticky, t->ctx.started, t_state,
jl_atomic_load_relaxed(&t->tid) + 1);
- jlbacktracet(t);
- jl_safe_printf(" ---- End task %zu\n", j + 1);
+ if (t->ctx.stkbuf != NULL)
+ jlbacktracet(t);
+ else
+ jl_safe_printf("thread (%d) no stack\n", ctid);
+ jl_safe_printf("thread (%d) ---- End task %zu\n", ctid, j + 1);
}
- jl_safe_printf("==== End thread %d\n", ptls2->tid + 1);
+ jl_safe_printf("thread (%d) ==== End thread %d\n", ctid, ptls2->tid + 1);
+ }
+ jl_safe_printf("thread (%d) ++++ Done\n", ctid);
+
+ if (!jl_inside_heartbeat_thread()) {
+ jl_heartbeat_resume();
}
- jl_safe_printf("==== Done\n");
}
#ifdef __cplusplus
diff --git a/src/task.c b/src/task.c
index 019a301b1f062..bb86bce48101a 100644
--- a/src/task.c
+++ b/src/task.c
@@ -203,10 +203,6 @@ static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt
lastt->ctx.copy_stack = nb;
lastt->sticky = 1;
memcpy_stack_a16((uint64_t*)buf, (uint64_t*)frame_addr, nb);
- // this task's stack could have been modified after
- // it was marked by an incremental collection
- // move the barrier back instead of walking it again here
- jl_gc_wb_back(lastt);
}
JL_NO_ASAN static void NOINLINE JL_NORETURN restore_stack(jl_ucontext_t *t, jl_ptls_t ptls, char *p)
@@ -504,6 +500,12 @@ JL_NO_ASAN static void ctx_switch(jl_task_t *lastt)
lastt->ctx.ctx = &lasttstate.ctx;
}
}
+ // this task's stack or scope field could have been modified after
+ // it was marked by an incremental collection
+ // move the barrier back instead of walking the shadow stack again here to check if that is required
+ // even if killed (dropping the stack) and just the scope field matters,
+ // let the gc figure that out next time it does a quick mark
+ jl_gc_wb_back(lastt);
// set up global state for new task and clear global state for old task
t->ptls = ptls;
@@ -1108,6 +1110,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion
jl_atomic_store_relaxed(&t->_isexception, 0);
// Inherit scope from parent task
t->scope = ct->scope;
+ jl_gc_wb_fresh(t, t->scope);
// Fork task-local random state from parent
jl_rng_split(t->rngState, ct->rngState);
// there is no active exception handler available on this stack yet
@@ -1573,6 +1576,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi)
ct->donenotify = jl_nothing;
jl_atomic_store_relaxed(&ct->_isexception, 0);
ct->scope = jl_nothing;
+ jl_gc_wb_knownold(ct, ct->scope);
ct->eh = NULL;
ct->gcstack = NULL;
ct->excstack = NULL;
diff --git a/src/threading.c b/src/threading.c
index 9f5c18fe53555..e56aba2de51a1 100644
--- a/src/threading.c
+++ b/src/threading.c
@@ -1111,6 +1111,289 @@ JL_DLLEXPORT int jl_setaffinity(int16_t tid, char *mask, int cpumasksize) {
return 0; // success
}
+// Heartbeat mechanism for Julia's task scheduler
+// ---
+// Start a thread that does not participate in running Julia's tasks. This
+// thread simply sleeps until the heartbeat mechanism is enabled. When
+// enabled, the heartbeat thread enters a loop in which it blocks waiting
+// for the specified heartbeat interval. If, within that interval,
+// `jl_heartbeat()` is *not* called at least once, then the thread calls
+// `jl_print_task_backtraces(0)`.
+
+#ifdef JL_HEARTBEAT_THREAD
+
+#include
+
+volatile int heartbeat_enabled;
+int heartbeat_tid; // Mostly used to ensure we skip this thread in the CPU profiler. XXX: not implemented on Windows
+uv_thread_t heartbeat_uvtid;
+uv_sem_t heartbeat_on_sem, // jl_heartbeat_enable -> thread
+ heartbeat_off_sem; // thread -> jl_heartbeat_enable
+int heartbeat_interval_s,
+ tasks_after_n,
+ reset_tasks_after_n;
+int tasks_showed, n_hbs_missed, n_hbs_recvd;
+_Atomic(int) heartbeats;
+
+JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT;
+void jl_heartbeat_threadfun(void *arg);
+
+// start the heartbeat thread with heartbeats disabled
+void jl_init_heartbeat(void)
+{
+ heartbeat_enabled = 0;
+ uv_sem_init(&heartbeat_on_sem, 0);
+ uv_sem_init(&heartbeat_off_sem, 0);
+ uv_thread_create(&heartbeat_uvtid, jl_heartbeat_threadfun, NULL);
+ uv_thread_detach(&heartbeat_uvtid);
+}
+
+int jl_inside_heartbeat_thread(void)
+{
+ uv_thread_t curr_uvtid = uv_thread_self();
+ return curr_uvtid == heartbeat_uvtid;
+}
+
+// enable/disable heartbeats
+// heartbeat_s: interval within which jl_heartbeat() must be called
+// show_tasks_after_n: number of heartbeats missed before printing task backtraces
+// reset_after_n: number of heartbeats after which to reset
+//
+// When disabling heartbeats, the heartbeat thread must wake up,
+// find out that heartbeats are now disabled, and reset. For now, we
+// handle this by preventing re-enabling of heartbeats until this
+// completes.
+JL_DLLEXPORT int jl_heartbeat_enable(int heartbeat_s, int show_tasks_after_n,
+ int reset_after_n)
+{
+ if (heartbeat_s <= 0) {
+ heartbeat_enabled = 0;
+ heartbeat_interval_s = tasks_after_n = reset_tasks_after_n = 0;
+ }
+ else {
+ // must disable before enabling
+ if (heartbeat_enabled) {
+ return -1;
+ }
+ // heartbeat thread must be ready
+ if (uv_sem_trywait(&heartbeat_off_sem) != 0) {
+ return -1;
+ }
+
+ jl_atomic_store_relaxed(&heartbeats, 0);
+ heartbeat_interval_s = heartbeat_s;
+ tasks_after_n = show_tasks_after_n;
+ reset_tasks_after_n = reset_after_n;
+ tasks_showed = 0;
+ n_hbs_missed = 0;
+ n_hbs_recvd = 0;
+ heartbeat_enabled = 1;
+ uv_sem_post(&heartbeat_on_sem); // wake the heartbeat thread
+ }
+ return 0;
+}
+
+// temporarily pause the heartbeat thread
+JL_DLLEXPORT int jl_heartbeat_pause(void)
+{
+ if (!heartbeat_enabled) {
+ return -1;
+ }
+ heartbeat_enabled = 0;
+ return 0;
+}
+
+// resume the paused heartbeat thread
+JL_DLLEXPORT int jl_heartbeat_resume(void)
+{
+ // cannot resume if the heartbeat thread is already running
+ if (heartbeat_enabled) {
+ return -1;
+ }
+
+ // cannot resume if we weren't paused (disabled != paused)
+ if (heartbeat_interval_s == 0) {
+ return -1;
+ }
+
+ // heartbeat thread must be ready
+ if (uv_sem_trywait(&heartbeat_off_sem) != 0) {
+ return -1;
+ }
+
+ // reset state as we've been paused
+ n_hbs_missed = 0;
+ n_hbs_recvd = 0;
+ tasks_showed = 0;
+
+ // resume
+ heartbeat_enabled = 1;
+ uv_sem_post(&heartbeat_on_sem); // wake the heartbeat thread
+ return 0;
+}
+
+// heartbeat
+JL_DLLEXPORT void jl_heartbeat(void)
+{
+ jl_atomic_fetch_add(&heartbeats, 1);
+}
+
+// sleep the thread for the specified interval
+void sleep_for(int secs, int nsecs)
+{
+ struct timespec rqtp, rmtp;
+ rqtp.tv_sec = secs;
+ rqtp.tv_nsec = nsecs;
+ rmtp.tv_sec = 0;
+ rmtp.tv_nsec = 0;
+ for (; ;) {
+ // this suspends the thread so we aren't using CPU
+ if (nanosleep(&rqtp, &rmtp) == 0) {
+ return;
+ }
+ // TODO: else if (errno == EINTR)
+ // this could be SIGTERM and we should shutdown but how to find out?
+ rqtp = rmtp;
+ }
+}
+
+// check for heartbeats and maybe report loss
+uint8_t check_heartbeats(uint8_t gc_state)
+{
+ int hb = jl_atomic_exchange(&heartbeats, 0);
+
+ if (hb <= 0) {
+ // we didn't get a heartbeat
+ n_hbs_recvd = 0;
+ n_hbs_missed++;
+
+ // if we've printed task backtraces already, do nothing
+ if (!tasks_showed) {
+ // otherwise, at least show this message
+ jl_safe_printf("==== heartbeat loss (%ds) ====\n",
+ n_hbs_missed * heartbeat_interval_s);
+ // if we've missed enough heartbeats, print task backtraces
+ if (n_hbs_missed >= tasks_after_n) {
+ jl_task_t *ct = jl_current_task;
+ jl_ptls_t ptls = ct->ptls;
+
+ // exit GC-safe region to report then re-enter
+ jl_gc_safe_leave(ptls, gc_state);
+ jl_print_task_backtraces(0);
+ gc_state = jl_gc_safe_enter(ptls);
+
+ // we printed task backtraces
+ tasks_showed = 1;
+ }
+ }
+ }
+ else {
+ // got a heartbeat
+ n_hbs_recvd++;
+ // if we'd printed task backtraces, check for reset
+ if (tasks_showed && n_hbs_recvd >= reset_tasks_after_n) {
+ tasks_showed = 0;
+ jl_safe_printf("==== heartbeats recovered (lost for %ds) ====\n",
+ n_hbs_missed * heartbeat_interval_s);
+ }
+ n_hbs_missed = 0;
+ }
+
+ return gc_state;
+}
+
+// heartbeat thread function
+void jl_heartbeat_threadfun(void *arg)
+{
+ int s = 59, ns = 1e9 - 1, rs;
+ uint64_t t0, tchb;
+
+ // We need a TLS because backtraces are accumulated into ptls->bt_size
+ // and ptls->bt_data, so we need to call jl_adopt_thread().
+ jl_adopt_thread();
+ (void)jl_atomic_fetch_add_relaxed(&n_threads_running, -1);
+ jl_task_t *ct = jl_current_task;
+ jl_ptls_t ptls = ct->ptls;
+ heartbeat_tid = ptls->tid;
+
+ // Don't hold up GC, this thread doesn't participate.
+ uint8_t gc_state = jl_gc_safe_enter(ptls);
+
+ for (;;) {
+ if (!heartbeat_enabled) {
+ // post the off semaphore to indicate we're ready to enable
+ uv_sem_post(&heartbeat_off_sem);
+
+ // sleep the thread here; this semaphore is posted in
+ // jl_heartbeat_enable() or jl_heartbeat_resume()
+ uv_sem_wait(&heartbeat_on_sem);
+
+ // Set the sleep duration.
+ s = heartbeat_interval_s - 1;
+ ns = 1e9 - 1;
+ continue;
+ }
+
+ // heartbeat is enabled; sleep, waiting for the desired interval
+ sleep_for(s, ns);
+
+ // if heartbeats were turned off/paused while we were sleeping, reset
+ if (!heartbeat_enabled) {
+ continue;
+ }
+
+ // check if any heartbeats have happened, report as appropriate
+ t0 = jl_hrtime();
+ gc_state = check_heartbeats(gc_state);
+ tchb = jl_hrtime() - t0;
+
+ // adjust the next sleep duration based on how long the heartbeat
+ // check took, but if it took too long then use the normal duration
+ rs = 1;
+ while (tchb > 1e9) {
+ rs++;
+ tchb -= 1e9;
+ }
+ if (rs < heartbeat_interval_s) {
+ s = heartbeat_interval_s - rs;
+ }
+ ns = 1e9 - tchb;
+ }
+}
+
+#else // !JL_HEARTBEAT_THREAD
+
+void jl_init_heartbeat(void)
+{
+}
+
+int jl_inside_heartbeat_thread(void)
+{
+ return 0;
+}
+
+JL_DLLEXPORT int jl_heartbeat_enable(int heartbeat_s, int show_tasks_after_n,
+ int reset_after_n)
+{
+ return -1;
+}
+
+JL_DLLEXPORT int jl_heartbeat_pause(void)
+{
+ return -1;
+}
+
+JL_DLLEXPORT int jl_heartbeat_resume(void)
+{
+ return -1;
+}
+
+JL_DLLEXPORT void jl_heartbeat(void)
+{
+}
+
+#endif // JL_HEARTBEAT_THREAD
+
#ifdef __cplusplus
}
#endif
diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl
index 4af706606d326..0227d9532a49c 100644
--- a/stdlib/Artifacts/src/Artifacts.jl
+++ b/stdlib/Artifacts/src/Artifacts.jl
@@ -16,6 +16,13 @@ using Base.TOML: TOML
export artifact_exists, artifact_path, artifact_meta, artifact_hash,
select_downloadable_artifacts, find_artifacts_toml, @artifact_str
+const _artifacts_world_age = Ref{UInt}(typemax(UInt))
+
+function __init__()
+ _artifacts_world_age[] = Base.get_world_counter()
+ nothing
+end
+
"""
parse_toml(path::String)
@@ -543,7 +550,7 @@ function jointail(dir, tail)
end
function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dict, hash, platform, ::Val{LazyArtifacts}) where LazyArtifacts
- world = Base._require_world_age[]
+ world = _artifacts_world_age[]
if world == typemax(UInt)
world = Base.get_world_counter()
end
diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version
index 3b1f7cc5847d9..6caafbaf313ac 100644
--- a/stdlib/LinearAlgebra.version
+++ b/stdlib/LinearAlgebra.version
@@ -1,4 +1,4 @@
LINEARALGEBRA_BRANCH = release-1.12
-LINEARALGEBRA_SHA1 = 997c4b7e7664f30645ef8bd51d8a4203e49c4631
+LINEARALGEBRA_SHA1 = 7249e662d66b53da6c9335ea748eac2750c2924f
LINEARALGEBRA_GIT_URL := https://github.com/JuliaLang/LinearAlgebra.jl.git
LINEARALGEBRA_TAR_URL = https://api.github.com/repos/JuliaLang/LinearAlgebra.jl/tarball/$1
diff --git a/stdlib/Logging/test/runtests.jl b/stdlib/Logging/test/runtests.jl
index 3e92b7d9e2697..a0ba9f7c43b1e 100644
--- a/stdlib/Logging/test/runtests.jl
+++ b/stdlib/Logging/test/runtests.jl
@@ -19,6 +19,13 @@ macro customlog(exs...) Base.CoreLogging.logmsg_code((Base.CoreLogging.@_sourcei
@test :handle_message in names(Logging, all=true) # non-exported public function
end
+@testset "LogLevel compatibility with integers" begin
+ @test Logging.Debug + 1000 == Logging.Info
+ @test Logging.Warn - 1000 == Logging.Info
+ @test Logging.Info < 500
+ @test 500 < Logging.Warn
+end
+
@testset "ConsoleLogger" begin
# First pass log limiting
@test min_enabled_level(ConsoleLogger(devnull, Logging.Debug)) == Logging.Debug
diff --git a/stdlib/Manifest.toml b/stdlib/Manifest.toml
index 3db51cf75328d..14482fd3001b6 100644
--- a/stdlib/Manifest.toml
+++ b/stdlib/Manifest.toml
@@ -240,6 +240,11 @@ weakdeps = ["SparseArrays"]
uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
version = "1.11.0"
+[[deps.SuiteSparse]]
+deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"]
+uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"
+version = "1.12.0"
+
[[deps.SuiteSparse_jll]]
deps = ["Artifacts", "Libdl", "libblastrampoline_jll"]
uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
diff --git a/stdlib/MozillaCACerts_jll/Project.toml b/stdlib/MozillaCACerts_jll/Project.toml
index 57c5526a6f1f2..d63251d59d58f 100644
--- a/stdlib/MozillaCACerts_jll/Project.toml
+++ b/stdlib/MozillaCACerts_jll/Project.toml
@@ -1,7 +1,7 @@
name = "MozillaCACerts_jll"
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
# Keep in sync with `deps/libgit2.version`.
-version = "2025.05.20"
+version = "2025.11.04"
[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version
index 6feeb870e2f20..853940e299a8e 100644
--- a/stdlib/Pkg.version
+++ b/stdlib/Pkg.version
@@ -1,4 +1,4 @@
PKG_BRANCH = release-1.12
-PKG_SHA1 = 53b2b5da91c27515ce129635fe184e8bd9afb09f
+PKG_SHA1 = b322a8ff786472d680a972e57575b4586fefe018
PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git
PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1
diff --git a/stdlib/Project.toml b/stdlib/Project.toml
index 1e03a0f474490..8a3e0f4d78e69 100644
--- a/stdlib/Project.toml
+++ b/stdlib/Project.toml
@@ -48,6 +48,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
SuiteSparse_jll = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
+SuiteSparse = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl
index a4d289db8cd40..21ca166b94a71 100644
--- a/stdlib/REPL/src/precompile.jl
+++ b/stdlib/REPL/src/precompile.jl
@@ -61,7 +61,7 @@ function repl_workload()
display("a string")
foo(x) = 1
@time @eval foo(1)
- ; pwd
+ ;
$CTRL_C
$CTRL_R$CTRL_C#
? reinterpret
diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version
index b6d9a820d9a06..98763ae71e777 100644
--- a/stdlib/SparseArrays.version
+++ b/stdlib/SparseArrays.version
@@ -1,4 +1,4 @@
SPARSEARRAYS_BRANCH = release-1.12
-SPARSEARRAYS_SHA1 = 5d674dc7bd90156cf8ecea4e143b69b5a5b7640d
+SPARSEARRAYS_SHA1 = f81a30d962b03d4048b26439d60979673e343b67
SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git
SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1
diff --git a/stdlib/stdlib.mk b/stdlib/stdlib.mk
index 006b7a276a3b3..1c418a7e1a8f4 100644
--- a/stdlib/stdlib.mk
+++ b/stdlib/stdlib.mk
@@ -6,8 +6,8 @@ INDEPENDENT_STDLIBS := \
ArgTools Base64 CRC32c Dates DelimitedFiles Distributed Downloads Future \
InteractiveUtils JuliaSyntaxHighlighting LazyArtifacts LibGit2 LibCURL Logging \
Markdown Mmap NetworkOptions Profile Printf Pkg REPL Serialization SharedArrays \
- SparseArrays Statistics StyledStrings SuiteSparse_jll Tar Test TOML Unicode UUIDs \
- dSFMT_jll GMP_jll libLLVM_jll LLD_jll LLVMLibUnwind_jll LibUnwind_jll LibUV_jll \
+ SparseArrays Statistics StyledStrings SuiteSparse_jll SuiteSparse Tar Test TOML \
+ Unicode UUIDs dSFMT_jll GMP_jll libLLVM_jll LLD_jll LLVMLibUnwind_jll LibUnwind_jll LibUV_jll \
LibCURL_jll LibSSH2_jll LibGit2_jll nghttp2_jll MozillaCACerts_jll \
MPFR_jll OpenLibm_jll OpenSSL_jll PCRE2_jll p7zip_jll Zlib_jll
diff --git a/test/scopedvalues.jl b/test/scopedvalues.jl
index e9b36d80fc2c4..500b9647f8da6 100644
--- a/test/scopedvalues.jl
+++ b/test/scopedvalues.jl
@@ -195,3 +195,74 @@ nothrow_scope()
push!(ts, 2)
end
end
+
+# LazyScopedValue
+global lsv_ncalled = 0
+const lsv = LazyScopedValue{Int}(OncePerProcess(() -> (global lsv_ncalled; lsv_ncalled += 1; 1)))
+@testset "LazyScopedValue" begin
+ @test (@with lsv=>2 lsv[]) == 2
+ @test lsv_ncalled == 0
+ @test lsv[] == 1
+ @test lsv_ncalled == 1
+ @test lsv[] == 1
+ @test lsv_ncalled == 1
+end
+
+@testset "ScopedThunk" begin
+ function check_svals()
+ @test sval[] == 8
+ @test sval_float[] == 8.0
+ end
+ sf = nothing
+ @with sval=>8 sval_float=>8.0 begin
+ sf = ScopedThunk(check_svals)
+ end
+ sf()
+ @with sval=>8 sval_float=>8.0 begin
+ sf2 = ScopedThunk{Function}(check_svals)
+ end
+ sf2()
+end
+
+using Base.ScopedValues: ScopedValue, with
+@noinline function test_59483()
+ sv = ScopedValue([])
+ ch = Channel{Bool}()
+
+ # Spawn a child task, which inherits the parent's Scope
+ @noinline function inner_function()
+ # Block until the parent task has left the scope.
+ take!(ch)
+ # Now, per issue 59483, this task's scope is not rooted, except by the task itself.
+
+ # Now switch to an inner scope, leaving the current scope possibly unrooted.
+ val = with(sv=>Any[2]) do
+ # Inside this new scope, when we perform GC, the parent scope can be freed.
+ # The fix for this issue made sure that the first scope in this task remains
+ # rooted.
+ GC.gc()
+ GC.gc()
+ sv[]
+ end
+ @test val == Any[2]
+ # Finally, we've returned to the original scope, but that could be a dangling
+ # pointer if the scope itself was freed by the above GCs. So these GCs could crash:
+ GC.gc()
+ GC.gc()
+ end
+ @noinline function spawn_inner()
+ # Set a new Scope in the parent task - this is the scope that could be freed.
+ with(sv=>Any[1]) do
+ return @async inner_function()
+ end
+ end
+
+ # RUN THE TEST:
+ t = spawn_inner()
+ # Exit the scope, and let the child task proceed
+ put!(ch, true)
+ wait(t)
+end
+@testset "issue 59483" begin
+ test_59483()
+end
diff --git a/test/syntax.jl b/test/syntax.jl
index 70640e6c93d67..b2d7ebfa96f98 100644
--- a/test/syntax.jl
+++ b/test/syntax.jl
@@ -3659,7 +3659,7 @@ end
@test p("public() = 6") == Expr(:(=), Expr(:call, :public), Expr(:block, 6))
end
-@testset "removing argument sideeffects" begin
+@testset "removing argument side effects" begin
# Allow let blocks in broadcasted LHSes, but only evaluate them once:
execs = 0
array = [1]
@@ -3675,6 +3675,15 @@ end
let; execs += 1; array; end::Vector{Int} .= 7
@test array == [7]
@test execs == 4
+
+ # remove argument side effects on lhs kwcall
+ pa_execs = 0
+ kw_execs = 0
+ f60152(v, pa; kw) = copy(v)
+ @test (f60152([1, 2, 3], 0; kw=0) .*= 2) == [2,4,6]
+ @test (f60152([1, 2, 3], (pa_execs+=1); kw=(kw_execs+=1)) .*= 2) == [2,4,6]
+ @test pa_execs === 1
+ @test kw_execs === 1
end
# Allow GlobalRefs in macro definition