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