From 949412520e287803eef0d090ccacbe5a9a877f10 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Thu, 11 Dec 2025 14:05:18 +0100 Subject: [PATCH 01/40] set VERSION to 1.12.3 (#60346) --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 6b89d58f861a7..81f363239f557 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.12.2 +1.12.3 From 966d0af0fdffc727eb240e2e4c908fdd46697e57 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 15 Dec 2025 12:20:38 +0100 Subject: [PATCH 02/40] release-1.12: Revert "build: More msys2 fixes (#59028)" (#60374) --- contrib/mac/app/Makefile | 4 ++-- deps/libgit2.mk | 11 +---------- deps/libssh2.mk | 5 ++++- deps/libuv.mk | 2 +- deps/llvm.mk | 8 -------- deps/openssl.mk | 18 ++++++++++-------- deps/tools/common.mk | 24 ++---------------------- src/Makefile | 12 +++++------- src/flisp/Makefile | 2 +- 9 files changed, 26 insertions(+), 60 deletions(-) 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/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/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/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/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/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 From 120dad24b56ff6478fdb93c62acd35eebcaa71dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= <765740+giordano@users.noreply.github.com> Date: Sat, 29 Nov 2025 08:00:21 +0000 Subject: [PATCH 03/40] Correct version in which at-lock was exported (#60279) It was exported in v1.7, not v1.10: #39588 (cherry picked from commit 1067db80b96cafb9da6864afbfdd9d6e27665c33) --- base/lock.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From a020033531491ace3f932447f76cde681ca795cd Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Tue, 2 Dec 2025 18:32:18 +0100 Subject: [PATCH 04/40] Fix buffer overflow in jloptions (#60299) Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com> (cherry picked from commit 841148d8a81066c4d4b93fac2f2d32aacad05fa7) --- src/jloptions.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jloptions.c b/src/jloptions.c index 96cce1c8d29a3..bb4092bb2845d 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -668,9 +668,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 From 986bb5ea6021006f9af38e441f4c8b6d0e4a2807 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 2 Dec 2025 15:55:14 -0500 Subject: [PATCH 05/40] build: include `SuiteSparse` in pkgimages to precompile (#60291) This seems to be an issue at least as far back as 1.10: ```julia $ rm -rf ~/.julia/compiled/*/SuiteSparse/ $ julia +1.10 -q julia> using SuiteSparse [ Info: Precompiling SuiteSparse [4607b0f0-06f3-5cda-b6b1-a6196a1729e9] ``` (cherry picked from commit d6f2a7e32e0991637e2e2f696c23494b75201a99) --- stdlib/Manifest.toml | 5 +++++ stdlib/Project.toml | 1 + stdlib/stdlib.mk | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) 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/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/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 From e35b957bb5caffeb1af708184714c9f99484a6d1 Mon Sep 17 00:00:00 2001 From: Eddie Groshev Date: Tue, 9 Dec 2025 23:33:24 -0800 Subject: [PATCH 06/40] Logging: define isless between Integer and LogLevel (#60330) (cherry picked from commit 5ee081631674166af22f3677d50c2fd56a2ccf48) --- base/logging/logging.jl | 2 ++ stdlib/Logging/test/runtests.jl | 7 +++++++ 2 files changed, 9 insertions(+) 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/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 From a16e9c97f1b551916832fb399e4fab4a81dd95eb Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:07:17 -0500 Subject: [PATCH 07/40] =?UTF-8?q?=F0=9F=A4=96=20[release-1.12]=20Bump=20Li?= =?UTF-8?q?nearAlgebra=20stdlib=20997c4b7=20=E2=86=92=20dd3ea72=20(#60395)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dkarrasch <26658441+dkarrasch@users.noreply.github.com> Fix GEMM dispatch for complex-real matmul (#1520) --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/LinearAlgebra.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/md5 delete mode 100644 deps/checksums/LinearAlgebra-997c4b7e7664f30645ef8bd51d8a4203e49c4631.tar.gz/sha512 create mode 100644 deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 create mode 100644 deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 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/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 b/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 new file mode 100644 index 0000000000000..b49795485f679 --- /dev/null +++ b/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 @@ -0,0 +1 @@ +89bf516f59c6e35cb0ca090d93fa347b diff --git a/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 b/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 new file mode 100644 index 0000000000000..3c87627b5a126 --- /dev/null +++ b/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 @@ -0,0 +1 @@ +286da5f2e9d6472f52e9eeee4ee9ec7b09bea8584fe95764de459b923c2323cb6ed6d9f04748c12c7f521a0a5bca07ec6ea69668e2f8e2a52595ce6a16f62410 diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version index 3b1f7cc5847d9..88a6d52a7b804 100644 --- a/stdlib/LinearAlgebra.version +++ b/stdlib/LinearAlgebra.version @@ -1,4 +1,4 @@ LINEARALGEBRA_BRANCH = release-1.12 -LINEARALGEBRA_SHA1 = 997c4b7e7664f30645ef8bd51d8a4203e49c4631 +LINEARALGEBRA_SHA1 = dd3ea72ec654706060220118f4a65a708733b84c LINEARALGEBRA_GIT_URL := https://github.com/JuliaLang/LinearAlgebra.jl.git LINEARALGEBRA_TAR_URL = https://api.github.com/repos/JuliaLang/LinearAlgebra.jl/tarball/$1 From be84d00fcd24947a2aa9475d4477a098be4ee870 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 16 Dec 2025 21:23:28 +0100 Subject: [PATCH 08/40] bump Pkg to latest 1.12 --- .../Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 | 1 + .../Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 | 1 + .../Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5 | 1 - .../Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 create mode 100644 deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-53b2b5da91c27515ce129635fe184e8bd9afb09f.tar.gz/sha512 diff --git a/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 b/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 new file mode 100644 index 0000000000000..c9ef7acb2b062 --- /dev/null +++ b/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 @@ -0,0 +1 @@ +54dc00afbb6c5bd7996cc351e0255be3 diff --git a/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 b/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 new file mode 100644 index 0000000000000..ec4cae79bd716 --- /dev/null +++ b/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 @@ -0,0 +1 @@ +e680f3342a2fef684783890788c392897b065c158c2c3cf7e9ce0b9ab0642b3c6f93b5af5cf6671c477bb32a5ceed981f1416a4ae6bb7fb100eb1e8832591750 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/stdlib/Pkg.version b/stdlib/Pkg.version index 6feeb870e2f20..48721c28ea983 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.12 -PKG_SHA1 = 53b2b5da91c27515ce129635fe184e8bd9afb09f +PKG_SHA1 = 1dad5c51322827d29d4654b0249415e83625cf4f PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From cb92daf3b6d6c708ad96baf37f4d7fa572a034f4 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 17 Dec 2025 12:20:26 +0100 Subject: [PATCH 09/40] use a "locally frozen world age" to invoke `_artifact_str` in (#60383) (cherry picked from commit 06ebe6e5ebd8f427cd7273c0a00277ff85e4f178) --- stdlib/Artifacts/src/Artifacts.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 From 496a23a563646dfecdac5100e9a3d5ccea072714 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Tue, 4 Nov 2025 17:34:25 -0500 Subject: [PATCH 10/40] MozillaCACerts: Update to 2025-11-04 (#60041) (cherry picked from commit 589a8c68b350191f7c51326a1952a6519b158c3c) --- deps/checksums/cacert-2025-11-04.pem/md5 | 1 + deps/checksums/cacert-2025-11-04.pem/sha512 | 1 + deps/libgit2.version | 2 +- stdlib/MozillaCACerts_jll/Project.toml | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 deps/checksums/cacert-2025-11-04.pem/md5 create mode 100644 deps/checksums/cacert-2025-11-04.pem/sha512 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.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/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" From 8086974f0601e08cdd59d1909f312a68e6f810dc Mon Sep 17 00:00:00 2001 From: Em Chu <61633163+mlechu@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:18:32 -0800 Subject: [PATCH 11/40] Fix `remove-argument-side-effects` with keyword call (#60195) Fix #60152, which impacted both lowering implementations. `remove-argument-side-effects` assumed all `kw` arguments from a `parameters` block had already been dumped into the argument list, which is not true in some cases. In addition: - JuliaLowering hit a MethodError in the dumped-`kw` case regardless. There are other issues with `kw` which I'm ignoring in this PR (see https://github.com/JuliaLang/julia/pull/60162) - Delete some ancient history: `&` [used to be a valid argument](https://github.com/JuliaLang/julia/commit/a378b750fd7e387f0504e7605c464944092e29e1#diff-5d79463faae0f7f19454c7f9888498d9f876082e258ab3efdca36a0ee64b0c87L72) head sometime in 2012 apparently! (cherry picked from commit 2be8847086ae4b44feb011204a93984294870156) --- src/julia-syntax.scm | 10 ++++++---- test/syntax.jl | 11 ++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) 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/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 From dda778899123e1e20a138783f962c8ebaba2a43a Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 3 Dec 2025 13:49:53 -0500 Subject: [PATCH 12/40] Make stdlib precompile failures throw (#60308) (cherry picked from commit 44ecbcf92df9658751d1d6b94f48f33375ef2b17) --- pkgimage.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 32ebc18afeec0fcb3d50cc5b9ca2df7231730281 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 18 Dec 2025 17:29:31 +0100 Subject: [PATCH 13/40] Fix world age docs link, point to correct manual version (manual backport to 1.12) (#60385) Manual backport of #60384 to `release-1.12` (there is currently no `backports-release-1.12` branch; once there is, this PR could of course be re-targeted to that). --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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`, From 4c54cafbd6db26108b3432b7c1906aaa0ff46e31 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:22:39 +0900 Subject: [PATCH 14/40] compiler: Fix typo in `abstract_invoke` (#60414) Fixed `method_ir_ci` typo in the `abstract_invoke`. --- Compiler/src/abstractinterpretation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From d4a8d2411639e777bb656408b7cb4620547f2e8b Mon Sep 17 00:00:00 2001 From: dkarrasch <26658441+dkarrasch@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:38:49 +0000 Subject: [PATCH 15/40] =?UTF-8?q?=F0=9F=A4=96=20[release-1.12]=20Bump=20Li?= =?UTF-8?q?nearAlgebra=20stdlib=20dd3ea72=20=E2=86=92=207249e66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - stdlib/LinearAlgebra.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/md5 create mode 100644 deps/checksums/LinearAlgebra-7249e662d66b53da6c9335ea748eac2750c2924f.tar.gz/sha512 delete mode 100644 deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 delete mode 100644 deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 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-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 b/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 deleted file mode 100644 index b49795485f679..0000000000000 --- a/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -89bf516f59c6e35cb0ca090d93fa347b diff --git a/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 b/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 deleted file mode 100644 index 3c87627b5a126..0000000000000 --- a/deps/checksums/LinearAlgebra-dd3ea72ec654706060220118f4a65a708733b84c.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -286da5f2e9d6472f52e9eeee4ee9ec7b09bea8584fe95764de459b923c2323cb6ed6d9f04748c12c7f521a0a5bca07ec6ea69668e2f8e2a52595ce6a16f62410 diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version index 88a6d52a7b804..6caafbaf313ac 100644 --- a/stdlib/LinearAlgebra.version +++ b/stdlib/LinearAlgebra.version @@ -1,4 +1,4 @@ LINEARALGEBRA_BRANCH = release-1.12 -LINEARALGEBRA_SHA1 = dd3ea72ec654706060220118f4a65a708733b84c +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 From d4ce3915d9cca25527dc718ecf4d36135ef8c43b Mon Sep 17 00:00:00 2001 From: IanButterworth <1694067+IanButterworth@users.noreply.github.com> Date: Sat, 20 Dec 2025 15:49:53 +0000 Subject: [PATCH 16/40] =?UTF-8?q?=F0=9F=A4=96=20[release-1.12]=20Bump=20Pk?= =?UTF-8?q?g=20stdlib=201dad5c513=20=E2=86=92=20b322a8ff7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 | 1 - .../Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 | 1 - .../Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 | 1 + .../Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 | 1 + stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/md5 create mode 100644 deps/checksums/Pkg-b322a8ff786472d680a972e57575b4586fefe018.tar.gz/sha512 diff --git a/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 b/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 deleted file mode 100644 index c9ef7acb2b062..0000000000000 --- a/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -54dc00afbb6c5bd7996cc351e0255be3 diff --git a/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 b/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 deleted file mode 100644 index ec4cae79bd716..0000000000000 --- a/deps/checksums/Pkg-1dad5c51322827d29d4654b0249415e83625cf4f.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e680f3342a2fef684783890788c392897b065c158c2c3cf7e9ce0b9ab0642b3c6f93b5af5cf6671c477bb32a5ceed981f1416a4ae6bb7fb100eb1e8832591750 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/stdlib/Pkg.version b/stdlib/Pkg.version index 48721c28ea983..853940e299a8e 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.12 -PKG_SHA1 = 1dad5c51322827d29d4654b0249415e83625cf4f +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 From 70af0056b88b4fa1422aae18eeb3316ff4506e4e Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Sat, 6 Dec 2025 05:59:43 -0800 Subject: [PATCH 17/40] Don't create a subprocess in the REPL precompile script (#60326) (cherry picked from commit 18aa2377728fb6c4f5b2b388f76690c1d3f8e3cb) --- stdlib/REPL/src/precompile.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From fa60880cabc073499a1c001afd0868cfe9fd04ad Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Mon, 22 Dec 2025 09:26:30 -0500 Subject: [PATCH 18/40] [backports-release-1.12] Fix 7z rpath (#60419) fix #60220 --- Make.inc | 2 +- Makefile | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) 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. From 3550e0ba3a4cfdd89378f7a491c5f1a43d1511e7 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 23 Dec 2025 06:43:54 +0100 Subject: [PATCH 19/40] Fix JET warning in `_spawn_primitive` (#60452) In various packages I am JETing, I see the same error being reported: > local variable `pp` may be undefined: pp::Base.Process This patch helps JET proof that there is no actual problem. Would be kinda nice to see this backported to 1.12 so that if I run JET with 1.12.4 this is not reported anymore. And of course also to 1.13. --- base/process.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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()) From 3d9bb7f7a7e4afb9d2ef577ecaeaad4af5d7071c Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 23 Dec 2025 06:50:42 +0100 Subject: [PATCH 20/40] Fix JET warnings in `copy_chunks!`, `copy_chunks_rtol!`, `fill_chunks!` (#60453) > local variable `msk_d1` may be undefined: msk_d1::UInt64 The code logic was actually right, but by transforming the code, JET can also see it (and arguably it is now also easier for a human to see that everything is correct) --- base/bitarray.jl | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) 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 From 5f7843a1886f1e868c365e633fbed01e73d44c13 Mon Sep 17 00:00:00 2001 From: dkarrasch <26658441+dkarrasch@users.noreply.github.com> Date: Tue, 23 Dec 2025 16:46:52 +0000 Subject: [PATCH 21/40] =?UTF-8?q?=F0=9F=A4=96=20[release-1.12]=20Bump=20Sp?= =?UTF-8?q?arseArrays=20stdlib=205d674dc=20=E2=86=92=20f81a30d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/SparseArrays.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/md5 delete mode 100644 deps/checksums/SparseArrays-5d674dc7bd90156cf8ecea4e143b69b5a5b7640d.tar.gz/sha512 create mode 100644 deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/md5 create mode 100644 deps/checksums/SparseArrays-f81a30d962b03d4048b26439d60979673e343b67.tar.gz/sha512 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/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 From 007794735619e02b53588acc93ef488f50fc2195 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 24 Dec 2025 08:23:46 +0100 Subject: [PATCH 22/40] Fix JET warning in `TOML.parse_array` (#60465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a bonus, also change `T == BigInt` to `T === BigInt`. Fixes this: ``` │┌ load_artifacts_toml(artifacts_toml::String) @ Artifacts /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-honeycrisp-R17H3W25T9.0/build/default-honeycrisp-R17H3W25T9-0/julialang/julia-release-1-dot-12/usr/share/julia/stdlib/v1.12/Artifacts/src/Artifacts.jl:315 ││┌ load_artifacts_toml(artifacts_toml::String; pkg_uuid::Nothing) @ Artifacts /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-honeycrisp-R17H3W25T9.0/build/default-honeycrisp-R17H3W25T9-0/julialang/julia-release-1-dot-12/usr/share/julia/stdlib/v1.12/Artifacts/src/Artifacts.jl:317 │││┌ parse_toml(path::String) @ Artifacts /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-honeycrisp-R17H3W25T9.0/build/default-honeycrisp-R17H3W25T9-0/julialang/julia-release-1-dot-12/usr/share/julia/stdlib/v1.12/Artifacts/src/Artifacts.jl:26 ││││┌ parsed_toml(project_file::String) @ Base ./loading.jl:276 │││││┌ parsed_toml(project_file::String, toml_cache::Base.TOMLCache{nothing}, toml_lock::ReentrantLock) @ Base ./loading.jl:278 ││││││┌ lock(f::Base.var"#parsed_toml##0#parsed_toml##1"{String, Base.TOMLCache{nothing}}, l::ReentrantLock) @ Base ./lock.jl:335 │││││││┌ (::Base.var"#parsed_toml##0#parsed_toml##1"{String, Base.TOMLCache{nothing}})() @ Base ./loading.jl:281 ││││││││┌ Base.CachedTOMLDict(p::Base.TOML.Parser{nothing}, path::String) @ Base ./loading.jl:222 │││││││││┌ parse(l::Base.TOML.Parser{nothing}) @ Base.TOML ./toml_parser.jl:444 ││││││││││┌ tryparse(l::Base.TOML.Parser{nothing}) @ Base.TOML ./toml_parser.jl:453 │││││││││││┌ parse_toplevel(l::Base.TOML.Parser{nothing}) @ Base.TOML ./toml_parser.jl:157 ││││││││││││┌ parse_array(l::Base.TOML.Parser{nothing}) @ Base.TOML ./toml_parser.jl:719 │││││││││││││┌ getproperty(x::Nothing, f::Symbol) @ Base ./Base_compiler.jl:54 ││││││││││││││ invalid builtin function call: getfield(x::Nothing, f::Symbol) │││││││││││││└──────────────────── ``` --- base/toml_parser.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 5b6e9d0b013dd954271c6a6abc17cd7ddf6d8858 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 24 Dec 2025 08:24:47 +0100 Subject: [PATCH 23/40] Fix JET warning in Base.sendfile (#60466) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Help JET (and humans?) understand that src_file and dst_file won't be used without being defined. Fixes this JET report: ``` │┌ cp(src::AbstractString, dst::String) @ Base.Filesystem ./file.jl:404 ││┌ cp(src::AbstractString, dst::String; force::Bool, follow_symlinks::Bool) @ Base.Filesystem ./file.jl:410 │││┌ kwcall(::@NamedTuple{force::Bool, follow_symlinks::Bool}, ::typeof(Base.Filesystem.cptree), src::String, dst::String) @ Base.Filesystem ./file.jl:366 ││││┌ cptree(src::String, dst::String; force::Bool, follow_symlinks::Bool) @ Base.Filesystem ./file.jl:379 │││││┌ sendfile(src::String, dst::String) @ Base.Filesystem ./file.jl:1282 ││││││ local variable `src_file` may be undefined: src_file::Base.Filesystem.File │││││└──────────────────── │││││┌ sendfile(src::String, dst::String) @ Base.Filesystem ./file.jl:1283 ││││││ local variable `src_file` may be undefined: src_file::Base.Filesystem.File │││││└──────────────────── │││││┌ sendfile(src::String, dst::String) @ Base.Filesystem ./file.jl:1285 ││││││ local variable `dst_file` may be undefined: dst_file::Base.Filesystem.File │││││└──────────────────── │││││┌ sendfile(src::String, dst::String) @ Base.Filesystem ./file.jl:1286 ││││││ local variable `dst_file` may be undefined: dst_file::Base.Filesystem.File │││││└──────────────────── ``` --- base/file.jl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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 From 820919812985c63e095e179b599dd70db04ea768 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sat, 27 Dec 2025 10:34:59 +0100 Subject: [PATCH 24/40] Fix JET warning related to `_array_for` (#60461) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calls to this method are produced by syntax lowering for simple typed comprehensions. The current signature is ambiguous, making JET believe that the modified method could have ended up invoking itself. Resolve this by renaming the other methods. These are the the warnings I am seeing while JETing a package: ``` ││┌ _array_for(::Type{T}, itr::AbstractVector{T} where T<:NCRingElement, isz::Any) where T @ Base ./array.jl:673 │││┌ _array_for(::Type, itr::Base.SizeUnknown, isz::Any) @ Base ./array.jl:673 ││││┌ _similar_shape(itr::Base.SizeUnknown, ::Base.HasLength) @ Base ./array.jl:657 │││││ no matching method found `length(::Base.SizeUnknown)`: length(itr::Base.SizeUnknown) ││││└──────────────────── ││││┌ _similar_shape(itr::Base.SizeUnknown, ::Base.HasShape) @ Base ./array.jl:658 │││││┌ axes(A::Base.SizeUnknown) @ Base ./abstractarray.jl:98 ││││││ no matching method found `size(::Base.SizeUnknown)`: size(A::Base.SizeUnknown) │││││└──────────────────── ``` and ``` ││││││││┌ Base.AnnotatedString(s::String, annots::Any) @ Base ./strings/annotated.jl:107 │││││││││┌ collect(::Type{@NamedTuple{region::UnitRange{Int64}, label::Symbol, value}}, itr::Any) @ Base ./array.jl:641 ││││││││││┌ _collect(::Type{@NamedTuple{…}}, itr::Any, isz::Union{Base.HasLength, Base.HasShape}) @ Base ./array.jl:643 │││││││││││┌ _array_for(::Type{@NamedTuple{region::UnitRange{Int64}, label::Symbol, value}}, itr::Base.HasLength, isz::Any) @ Base ./array.jl:673 ││││││││││││┌ _similar_shape(itr::Base.HasLength, ::Base.HasLength) @ Base ./array.jl:657 │││││││││││││ no matching method found `length(::Base.HasLength)`: length(itr::Base.HasLength) ││││││││││││└──────────────────── ││││││││││││┌ _similar_shape(itr::Base.HasLength, ::Base.HasShape) @ Base ./array.jl:658 │││││││││││││┌ axes(A::Base.HasLength) @ Base ./abstractarray.jl:98 ││││││││││││││ no matching method found `size(::Base.HasLength)`: size(A::Base.HasLength) │││││││││││││└──────────────────── ││││││││││││┌ _array_for(::Type{@NamedTuple{region::UnitRange{Int64}, label::Symbol, value}}, itr::Base.HasLength, isz::Nothing) @ Base ./array.jl:673 │││││││││││││ no matching method found `_similar_shape(::Base.HasLength, ::Nothing)`: Base._similar_shape(itr::Base.HasLength, isz::Nothing) ││││││││││││└──────────────────── ``` --- base/array.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 From 5ff524694c94df59362318e35cdaa3ba391e9fbf Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 6 Jan 2026 17:56:43 +0100 Subject: [PATCH 25/40] set VERSION to 1.12.4 (#60539) --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 81f363239f557..89c881bc9cb92 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.12.3 +1.12.4 From 888716d7231645fbb03d94bee3fbc9b6ec473307 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Fri, 5 Dec 2025 09:10:31 -0700 Subject: [PATCH 26/40] RAI: Add 'RAI' as the build part of the semantic version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 89c881bc9cb92..032ff3c8bc26f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.12.4 +1.12.4+RAI From 9b13c8810cdecd168db299dab2ab9322a486010f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Drvo=C5=A1t=C4=9Bp?= Date: Tue, 19 Mar 2024 18:35:13 +0100 Subject: [PATCH 27/40] Add GitHub template and workflows needed on the default branch (#135) --- .github/labeler.yml | 3 ++ .github/pull_request_template.md | 14 ++++++++++ .github/workflows/labeler.yml | 17 +++++++++++ .github/workflows/stale.yml | 16 +++++++++++ .../workflows/update-upstream-branches.yml | 28 +++++++++++++++++++ 5 files changed, 78 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/labeler.yml create mode 100644 .github/workflows/stale.yml create mode 100644 .github/workflows/update-upstream-branches.yml 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 }} From cba43edb3cc4f79e5a4ce57c28874160ac3b4912 Mon Sep 17 00:00:00 2001 From: d-netto Date: Mon, 6 Jan 2025 10:06:00 -0300 Subject: [PATCH 28/40] RAI: Track blocks and bytes allocated for GC pools --- src/gc-pages.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/gc-pages.c b/src/gc-pages.c index 71d59de29166f..7cb1fb1c33bc3 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) @@ -62,6 +84,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; } From 562b7654c23736ff82b18c6dda9d5289fded02d9 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Wed, 9 Aug 2023 12:57:35 -0400 Subject: [PATCH 29/40] RAI: Change task ordering behavior to prioritize older tasks --- base/partr.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 11325575557544d535420fa91225016454b2012b Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 21 Aug 2023 12:19:17 -0400 Subject: [PATCH 30/40] RAI: Disable huge pages for all mmap'ed memory Prevent transparent huge pages (THP) overallocating pysical memory. Co-authored-by: Adnan Alhomssi --- src/gc-pages.c | 5 +++++ src/gc-stacks.c | 6 ++++-- src/gc-stock.c | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/gc-pages.c b/src/gc-pages.c index 7cb1fb1c33bc3..f92a9ae828c12 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -77,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 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..dc3192a2b71d9 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -3906,6 +3906,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; From 68d5aa7f5308afbb238dc331746fd803e090301e Mon Sep 17 00:00:00 2001 From: "Bradley C. Kuszmaul" Date: Tue, 22 Aug 2023 14:27:07 -0400 Subject: [PATCH 31/40] RAI: Never use MADV_FREE --- src/gc-pages.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gc-pages.c b/src/gc-pages.c index f92a9ae828c12..79dd8993a8861 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -212,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) { From 15d8661f3a45ca278c8e12403e6e86a5a2c77807 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 9 Oct 2023 16:32:13 -0400 Subject: [PATCH 32/40] RAI: Prepend "thread (%d) " to output from `jl_print_task_backtraces()` --- src/stackwalk.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/stackwalk.c b/src/stackwalk.c index 50566b46ff45a..2a9dea558b191 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -1415,6 +1415,8 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT { size_t nthreads = jl_atomic_load_acquire(&jl_n_threads); jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); + int 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 +1432,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 +1457,20 @@ 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("==== Done\n"); + jl_safe_printf("thread (%d) ++++ Done\n", ctid); } #ifdef __cplusplus From c09c0622fb91dd756de97e32bf7a6c8f954b90a7 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Sat, 7 Oct 2023 18:30:01 -0400 Subject: [PATCH 33/40] RAI: Add heartbeat capability --- src/gc-stock.c | 5 + src/gc-stock.h | 3 + src/init.c | 7 ++ src/julia_internal.h | 2 + src/options.h | 3 + src/signals-mach.c | 4 + src/signals-unix.c | 4 + src/stackwalk.c | 19 ++- src/threading.c | 283 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 329 insertions(+), 1 deletion(-) diff --git a/src/gc-stock.c b/src/gc-stock.c index dc3192a2b71d9..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(); 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/init.c b/src/init.c index e11b360b5d378..a930ccad77fee 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) && diff --git a/src/julia_internal.h b/src/julia_internal.h index c9e1b0e204df6..cddd8deee9c42 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 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/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..550f3303aa2b1 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -873,6 +873,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; diff --git a/src/stackwalk.c b/src/stackwalk.c index 2a9dea558b191..410f4028b6704 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -1410,9 +1410,22 @@ 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 = jl_threadid() + 1; @@ -1471,6 +1484,10 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT 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(); + } } #ifdef __cplusplus 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 From 51d17069b24621bac62e05b4141fb4aada07886b Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:06:26 -0300 Subject: [PATCH 34/40] RAI: --safe-crash-log-file flag --- base/options.jl | 1 + src/init.c | 9 ++++++++ src/jl_uv.c | 54 +++++++++++++++++++++++++++++++++++++++++++ src/jloptions.c | 7 ++++++ src/jloptions.h | 1 + src/julia_internal.h | 26 +++++++++++++++++++++ src/signal-handling.c | 2 ++ src/signals-unix.c | 10 +------- src/signals-win.c | 2 +- 9 files changed, 102 insertions(+), 10 deletions(-) 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/src/init.c b/src/init.c index a930ccad77fee..a0d2719230e77 100644 --- a/src/init.c +++ b/src/init.c @@ -757,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/jl_uv.c b/src/jl_uv.c index a21b05433b8c6..140c022e98a29 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,56 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...) return c; } +STATIC_INLINE void print_error_msg_as_json(char *buf) JL_NOTSAFEPOINT +{ + // Our telemetry on SPCS expects a JSON object per line + // The following lines prepare the timestamp string and the JSON object + struct timeval tv; + struct tm* tm_info; + char timestamp_buffer[50]; + // Get current time + gettimeofday(&tv, NULL); + tm_info = gmtime(&tv.tv_sec); + // Format time + int offset = strftime(timestamp_buffer, 25, "%Y-%m-%dT%H:%M:%S", tm_info); + // Append milliseconds + snprintf(timestamp_buffer + offset, 25, ".%03d", tv.tv_usec / 1000); + const char *json_preamble_p1 = "\n{\"level\":\"Error\", \"timestamp\":\""; + const char *json_preamble_p2 = "\", \"message\": \""; + const char *json_postamble = "\"}\n"; + // Ignore write failures because there is nothing we can do + write(jl_sig_fd, json_preamble_p1, strlen(json_preamble_p1)); + write(jl_sig_fd, timestamp_buffer, strlen(timestamp_buffer)); + write(jl_sig_fd, json_preamble_p2, strlen(json_preamble_p2)); + // JSON escape the input string + for(size_t i = 0; i < strlen(buf); i += 1) { + switch (buf[i]) { + case '"': + write(jl_sig_fd, "\\\"", 2); + break; + case '\b': + write(jl_sig_fd, "\\b", 2); + break; + case '\n': + write(jl_sig_fd, "\\n", 2); + break; + case '\r': + write(jl_sig_fd, "\\r", 2); + break; + case '\t': + write(jl_sig_fd, "\\t", 2); + break; + case '\\': + write(jl_sig_fd, "\\\\", 2); + break; + default: + write(jl_sig_fd, buf + i, 1); + } + } + write(jl_sig_fd, json_postamble, strlen(json_postamble)); + fdatasync(jl_sig_fd); +} + JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...) { static char buf[1000]; @@ -829,6 +880,9 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...) va_end(args); buf[999] = '\0'; + if (jl_inside_signal_handler() && jl_sig_fd != 0) { + print_error_msg_as_json(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 bb4092bb2845d..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 } }; @@ -1013,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_internal.h b/src/julia_internal.h index cddd8deee9c42..64b98cada2ef3 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -782,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); diff --git a/src/signal-handling.c b/src/signal-handling.c index b03f0c1a430cd..6e3f22c87a075 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 // diff --git a/src/signals-unix.c b/src/signals-unix.c index 550f3303aa2b1..ec7610b1ab9a7 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) diff --git a/src/signals-win.c b/src/signals-win.c index c8ae74f52dba4..7f33eeb961624 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. From ea6c4f12a1bc5845bf1dcaa3d040f00948bb5421 Mon Sep 17 00:00:00 2001 From: Kiran Pamnany Date: Mon, 27 May 2024 15:39:36 -0400 Subject: [PATCH 35/40] RAI: Write heartbeat thread output to safe crash log --- src/jl_uv.c | 86 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/src/jl_uv.c b/src/jl_uv.c index 140c022e98a29..5d8ba54c06c4e 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -814,56 +814,83 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...) return c; } -STATIC_INLINE void print_error_msg_as_json(char *buf) JL_NOTSAFEPOINT +STATIC_INLINE int copystp(char *dest, const char *src) { - // Our telemetry on SPCS expects a JSON object per line - // The following lines prepare the timestamp string and the JSON object + 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; - char timestamp_buffer[50]; - // Get current time gettimeofday(&tv, NULL); tm_info = gmtime(&tv.tv_sec); - // Format time - int offset = strftime(timestamp_buffer, 25, "%Y-%m-%dT%H:%M:%S", tm_info); - // Append milliseconds - snprintf(timestamp_buffer + offset, 25, ".%03d", tv.tv_usec / 1000); - const char *json_preamble_p1 = "\n{\"level\":\"Error\", \"timestamp\":\""; - const char *json_preamble_p2 = "\", \"message\": \""; - const char *json_postamble = "\"}\n"; - // Ignore write failures because there is nothing we can do - write(jl_sig_fd, json_preamble_p1, strlen(json_preamble_p1)); - write(jl_sig_fd, timestamp_buffer, strlen(timestamp_buffer)); - write(jl_sig_fd, json_preamble_p2, strlen(json_preamble_p2)); - // JSON escape the input string - for(size_t i = 0; i < strlen(buf); i += 1) { + 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 '"': - write(jl_sig_fd, "\\\"", 2); + wlen += copystp(&wbuf[wlen], "\\\""); break; case '\b': - write(jl_sig_fd, "\\b", 2); + wlen += copystp(&wbuf[wlen], "\\b"); break; case '\n': - write(jl_sig_fd, "\\n", 2); + wlen += copystp(&wbuf[wlen], "\\n"); break; case '\r': - write(jl_sig_fd, "\\r", 2); + wlen += copystp(&wbuf[wlen], "\\r"); break; case '\t': - write(jl_sig_fd, "\\t", 2); + wlen += copystp(&wbuf[wlen], "\\t"); break; case '\\': - write(jl_sig_fd, "\\\\", 2); + wlen += copystp(&wbuf[wlen], "\\\\"); break; default: - write(jl_sig_fd, buf + i, 1); + wbuf[wlen++] = buf[i]; + break; } } - write(jl_sig_fd, json_postamble, strlen(json_postamble)); + // 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]; @@ -880,8 +907,11 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...) va_end(args); buf[999] = '\0'; - if (jl_inside_signal_handler() && jl_sig_fd != 0) { - print_error_msg_as_json(buf); + // 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 From 23c7ff3f18da9fdcb757c03d143b9e0ec7a28c9e Mon Sep 17 00:00:00 2001 From: d-netto Date: Mon, 6 Jan 2025 15:17:03 -0300 Subject: [PATCH 36/40] RAI: fix string interpolation on OPENBLAS_BUILD_OPTS --- deps/openblas.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 92c41490cd355adb835260229448f9f740257986 Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Mon, 21 Aug 2023 12:10:18 -0400 Subject: [PATCH 37/40] RAI: Prepend signal number and thread ID on backtraces Prepend `[signal (X) ]thread (Y) ` to each backtrace line that is displayed. Co-authored-by: Diogo Netto <61364108+d-netto@users.noreply.github.com> --- src/julia_internal.h | 6 +++--- src/safepoint.c | 2 +- src/signal-handling.c | 2 +- src/signals-unix.c | 2 +- src/signals-win.c | 2 +- src/stackwalk.c | 45 ++++++++++++++++++++++++++++--------------- 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/julia_internal.h b/src/julia_internal.h index 64b98cada2ef3..52832b32b25e0 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1527,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/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 6e3f22c87a075..269885bb5c14c 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -649,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-unix.c b/src/signals-unix.c index ec7610b1ab9a7..384bad2017126 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -1107,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(-1, signal_bt_data + i); } } } diff --git a/src/signals-win.c b/src/signals-win.c index 7f33eeb961624..d9c7ffd5ae769 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -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 410f4028b6704..ab283b85b59db 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,33 @@ 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] = '\0'; + if (sig != -1) { + snprintf(sig_str, 32, "signal (%d) ", sig); + } + 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 +783,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 +1372,9 @@ 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]; + 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 +1389,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 +1412,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"); From 2573b355f24f97937a21ae5b118b22e63c4fc67e Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Thu, 18 Apr 2024 16:36:27 -0400 Subject: [PATCH 38/40] RAI: do not prepend thread ID to backtraces from signal handler context Also show the signal number when we have it. --- src/signals-unix.c | 2 +- src/stackwalk.c | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index 384bad2017126..ed186d5b357cd 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -1107,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(-1, signal_bt_data + i); + jl_print_bt_entry_codeloc(sig, signal_bt_data + i); } } } diff --git a/src/stackwalk.c b/src/stackwalk.c index ab283b85b59db..b04142f7cf845 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -757,11 +757,14 @@ static void jl_print_debugloc(const char *pre_str, jl_debuginfo_t *debuginfo, jl 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] = '\0'; + sig_str[0] = pre_str[0] = '\0'; if (sig != -1) { snprintf(sig_str, 32, "signal (%d) ", sig); } - snprintf(pre_str, 64, "%sthread (%d) ", sig_str, jl_threadid() + 1); + // 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(pre_str, bt_entry[0].uintptr); @@ -1373,7 +1376,11 @@ JL_DLLEXPORT jl_record_backtrace_result_t jl_record_backtrace(jl_task_t *t, jl_b JL_DLLEXPORT void jl_gdblookup(void* ip) { char pre_str[64]; - snprintf(pre_str, 64, "thread (%d) ", jl_threadid() + 1); + 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); } @@ -1441,7 +1448,11 @@ JL_DLLEXPORT void jl_print_task_backtraces(int show_done) JL_NOTSAFEPOINT size_t nthreads = jl_atomic_load_acquire(&jl_n_threads); jl_ptls_t *allstates = jl_atomic_load_relaxed(&jl_all_tls_states); - int ctid = jl_threadid() + 1; + 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]; From be0c1bf6e905f51a1b26aaadfc7fdea2162fb635 Mon Sep 17 00:00:00 2001 From: Kiran Pamnany Date: Wed, 2 Jul 2025 12:28:26 -0400 Subject: [PATCH 39/40] Add `Base.isprecompilable` (#58805) Alternative to https://github.com/JuliaLang/julia/pull/58146. We want to compile a subset of the possible specializations of a function. To this end, we have a number of manually written `precompile` statements. Creating this list is, unfortunately, error-prone, and the list is also liable to going stale. Thus we'd like to validate each `precompile` statement in the list. The simple answer is, of course, to actually run the `precompile`s, and we naturally do so, but this takes time. We would like a relatively quick way to check the validity of a `precompile` statement. This is a dev-loop optimization, to allow us to check "is-precompilable" in unit tests. We can't use `hasmethod` as it has both false positives (too loose): ```julia julia> hasmethod(sum, (AbstractVector,)) true julia> precompile(sum, (AbstractVector,)) false julia> Base.isprecompilable(sum, (AbstractVector,)) # <- this PR false ``` and also false negatives (too strict): ```julia julia> bar(@nospecialize(x::AbstractVector{Int})) = 42 bar (generic function with 1 method) julia> hasmethod(bar, (AbstractVector,)) false julia> precompile(bar, (AbstractVector,)) true julia> Base.isprecompilable(bar, (AbstractVector,)) # <- this PR true ``` We can't use `hasmethod && isconcretetype` as it has false negatives (too strict): ```julia julia> has_concrete_method(f, argtypes) = all(isconcretetype, argtypes) && hasmethod(f, argtypes) has_concrete_method (generic function with 1 method) julia> has_concrete_method(bar, (AbstractVector,)) false julia> has_concrete_method(convert, (Type{Int}, Int32)) false julia> precompile(convert, (Type{Int}, Int32)) true julia> Base.isprecompilable(convert, (Type{Int}, Int32)) # <- this PR true ``` `Base.isprecompilable` is essentially `precompile` without the actual compilation. --- base/loading.jl | 14 ++++++++++++++ src/gf.c | 9 +++++++++ src/jl_exported_funcs.inc | 1 + 3 files changed, 24 insertions(+) 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/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/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) \ From 22ac37a55a0ad83d354109cc5001c03e4d4904f0 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Fri, 16 Jan 2026 09:11:48 -0700 Subject: [PATCH 40/40] Fix GC corruption via two upstream PRs. (#272) * Preserve the scope across the exception handler (#60647) We store the previous scope in the eh_state and thus hide it from GC. This means we need to manually preserve that scope across the `try ... catch`, instead fo the new scope that we switch to. --------- Co-authored-by: Nathan Daly Co-authored-by: Keno Fischer * add wb_back on all task switch paths (#60617) Since this task's stack or scope field could have been modified after it was marked by an incremental collection (and not just for copy stacks), move the barrier back unconditionally here. --------- Co-authored-by: Valentin Churavy Co-authored-by: Jeff Bezanson --------- Co-authored-by: Valentin Churavy Co-authored-by: Keno Fischer Co-authored-by: Jameson Nash Co-authored-by: Jeff Bezanson --- src/codegen.cpp | 9 +++--- src/gc-interface.h | 9 +++++- src/interpreter.c | 12 ++++---- src/rtutils.c | 2 ++ src/task.c | 12 +++++--- test/scopedvalues.jl | 71 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 15 deletions(-) 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/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/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/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/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/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