From 21944974bace3eb26969f903941be16204d5aa1c Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 4 Nov 2021 13:00:39 +0100 Subject: [PATCH 01/14] Reincarnate PR #7490 - Add warning when loading multiple copies of the same shared library --- base/Base.jl | 8 +++---- base/libdl.jl | 45 ++++++++++++++++++++++++++++++++++++++- stdlib/Libdl/src/Libdl.jl | 4 ++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index f044cf45fe97c..659bcef2032da 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -252,15 +252,15 @@ include("missing.jl") # version include("version.jl") +# Logging +include("logging.jl") +using .CoreLogging + # system & environment include("sysinfo.jl") include("libc.jl") using .Libc: getpid, gethostname, time -# Logging -include("logging.jl") -using .CoreLogging - # Concurrency include("linked_list.jl") include("condition.jl") diff --git a/base/libdl.jl b/base/libdl.jl index 4f29260bb24f8..3b8049b50fc26 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -9,7 +9,7 @@ import Base.DL_LOAD_PATH export DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, - dlpath, find_library, dlext, dllist + dlpath, find_library, dlext, dllist, check_dllist """ DL_LOAD_PATH @@ -76,6 +76,48 @@ function dlsym_e(hnd::Ptr, s::Union{Symbol,AbstractString}) return something(dlsym(hnd, s; throw_error=false), C_NULL) end +if Sys.isapple() + const dlpattern = r"^([^.]+).*\.dylib$" +elseif Sys.iswindows() + const dlpattern = r"^(.+)\.dll$" +else + #assume Sys.islinux, or similar + const dlpattern = r"^(.+?)\.so(?:\..*)?$" +end + +""" + dlname(fullpath::String) + +Returns the name of the library. +""" +function dlname(fullpath::String) + bn = basename(fullpath) + m = match(dlpattern, bn) + return isnothing(m) ? bn : m.captures[1] +end + +""" + check_dllist(; warnings = true)::Bool + +xxxxx + +TODO - from v1.8 +""" +function check_dllist(; warnings = true)::Bool + fullpaths = dllist() + names = dlname.(fullpaths) + noduplicity = true + for (i,dl) in enumerate(names), j in i+1:length(names) + if dl == names[j] + warn && @warn """detected possible duplicate library loaded: $dl. this may lead to unexpected behavior. +$(fullpaths[i]) +$(fullpaths[j])""" maxlog=1 + noduplicity = false + end + end + return noduplicity +end + """ dlopen(libfile::AbstractString [, flags::Integer]; throw_error:Bool = true) @@ -118,6 +160,7 @@ function dlopen(s::AbstractString, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; t if ret == C_NULL return nothing end + check_dllist() return ret end diff --git a/stdlib/Libdl/src/Libdl.jl b/stdlib/Libdl/src/Libdl.jl index df3f62c807fed..cbb026c2e00e4 100644 --- a/stdlib/Libdl/src/Libdl.jl +++ b/stdlib/Libdl/src/Libdl.jl @@ -4,10 +4,10 @@ module Libdl # Just re-export Base.Libc.Libdl: export DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, - dlpath, find_library, dlext, dllist + dlpath, find_library, dlext, dllist, check_dllist import Base.Libc.Libdl: DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, - dlpath, find_library, dlext, dllist + dlpath, find_library, dlext, dllist, check_dllist end # module From 6ad3aafdc21a527cb9ac8b1a80575988cecb2abf Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Mon, 8 Nov 2021 18:26:10 +0100 Subject: [PATCH 02/14] Update comments --- base/libdl.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index 3b8049b50fc26..fd9b082e4d953 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -99,9 +99,13 @@ end """ check_dllist(; warnings = true)::Bool -xxxxx +Check wheather the same shared library is loaded from two different files. +The `warnings` determines if the warnings are printed to the console. -TODO - from v1.8 +Returns if no duplicate was detected. + +!!! compat "Julia 1.8" + This method requires Julia 1.8 or later. """ function check_dllist(; warnings = true)::Bool fullpaths = dllist() @@ -109,7 +113,8 @@ function check_dllist(; warnings = true)::Bool noduplicity = true for (i,dl) in enumerate(names), j in i+1:length(names) if dl == names[j] - warn && @warn """detected possible duplicate library loaded: $dl. this may lead to unexpected behavior. + warnings && @warn """Detected possible duplicate library loaded: $dl +This may lead to unexpected behavior! $(fullpaths[i]) $(fullpaths[j])""" maxlog=1 noduplicity = false From e2d947f87d911376ae93854f30b0ba236c388741 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Tue, 9 Nov 2021 19:19:49 +0100 Subject: [PATCH 03/14] Make warning opt-out and disable them when symlinked the the same file. --- base/libdl.jl | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index fd9b082e4d953..1340fb8659340 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -103,24 +103,22 @@ Check wheather the same shared library is loaded from two different files. The `warnings` determines if the warnings are printed to the console. Returns if no duplicate was detected. - -!!! compat "Julia 1.8" - This method requires Julia 1.8 or later. """ -function check_dllist(; warnings = true)::Bool +function check_dllist() fullpaths = dllist() names = dlname.(fullpaths) - noduplicity = true + dlabspath(x) = isfile(x) ? abspath(realpath(x)) : x for (i,dl) in enumerate(names), j in i+1:length(names) if dl == names[j] - warnings && @warn """Detected possible duplicate library loaded: $dl + if dlabspath(fullpaths[i]) == dlabspath(fullpaths[j]) + continue + end + @warn """Detected possible duplicate library loaded: $dl This may lead to unexpected behavior! $(fullpaths[i]) $(fullpaths[j])""" maxlog=1 - noduplicity = false end end - return noduplicity end """ @@ -160,12 +158,12 @@ function dlopen end dlopen(s::Symbol, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; kwargs...) = dlopen(string(s), flags; kwargs...) -function dlopen(s::AbstractString, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; throw_error::Bool = true) +function dlopen(s::AbstractString, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; throw_error::Bool = true, warnings::Bool = true) ret = ccall(:jl_load_dynamic_library, Ptr{Cvoid}, (Cstring,UInt32,Cint), s, flags, Cint(throw_error)) if ret == C_NULL return nothing end - check_dllist() + warnings && check_dllist() return ret end From 0e9915d7db2b3800bb261a40445693cb850519ab Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Tue, 9 Nov 2021 19:23:58 +0100 Subject: [PATCH 04/14] Update comments --- base/libdl.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index 1340fb8659340..5d9998264749d 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -97,12 +97,10 @@ function dlname(fullpath::String) end """ - check_dllist(; warnings = true)::Bool + check_dllist() Check wheather the same shared library is loaded from two different files. The `warnings` determines if the warnings are printed to the console. - -Returns if no duplicate was detected. """ function check_dllist() fullpaths = dllist() From c543bdabb52379355f45f80a23a5c6acbf32034e Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Wed, 10 Nov 2021 09:12:27 +0100 Subject: [PATCH 05/14] Improve complexity to O(n^2 log n) --- base/libdl.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index 5d9998264749d..35013fce5f42f 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -105,16 +105,18 @@ The `warnings` determines if the warnings are printed to the console. function check_dllist() fullpaths = dllist() names = dlname.(fullpaths) + perm = sortperm(names) dlabspath(x) = isfile(x) ? abspath(realpath(x)) : x - for (i,dl) in enumerate(names), j in i+1:length(names) - if dl == names[j] - if dlabspath(fullpaths[i]) == dlabspath(fullpaths[j]) + for i in 1:length(names)-1 + p1, p2 = perm[i], perm[i+1] + if names[p1] == names[p2] + if dlabspath(fullpaths[p1]) == dlabspath(fullpaths[p2]) continue end - @warn """Detected possible duplicate library loaded: $dl + @warn """Detected possible duplicate library loaded: $(names[p1]) This may lead to unexpected behavior! -$(fullpaths[i]) -$(fullpaths[j])""" maxlog=1 +$(fullpaths[p1]) +$(fullpaths[p2])""" maxlog=1 end end end From e4642fe72ed8fd8ee4629e9f43855c2d6c0d20d6 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Wed, 10 Nov 2021 14:19:53 +0100 Subject: [PATCH 06/14] Cache dlname() outputs --- base/libdl.jl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index 35013fce5f42f..db8919c427ba7 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -85,15 +85,21 @@ else const dlpattern = r"^(.+?)\.so(?:\..*)?$" end +const _dlname_cache = Dict{String, SubString{String}}() + """ dlname(fullpath::String) Returns the name of the library. """ -function dlname(fullpath::String) +function dlname(fullpath::String)::SubString{String} + cache = get(_dlname_cache, fullpath, "") + cache != "" && return cache bn = basename(fullpath) m = match(dlpattern, bn) - return isnothing(m) ? bn : m.captures[1] + ret = isnothing(m) ? bn : m.captures[1] + _dlname_cache[fullpath] = ret + return ret end """ From 626b711b6bda9ea56bd5e1ed583999dabcf4b627 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Wed, 10 Nov 2021 15:58:22 +0100 Subject: [PATCH 07/14] Make PR thread-safe --- base/Base.jl | 6 ++++-- base/libdl.jl | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 8cde3e5a0e356..8f8bf577f638a 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -256,8 +256,6 @@ using .CoreLogging # system & environment include("sysinfo.jl") -include("libc.jl") -using .Libc: getpid, gethostname, time # Concurrency include("linked_list.jl") @@ -269,6 +267,10 @@ include("task.jl") include("threads_overloads.jl") include("weakkeydict.jl") +# Libdl +include("libc.jl") +using .Libc: getpid, gethostname, time + include("env.jl") # BinaryPlatforms, used by Artifacts diff --git a/base/libdl.jl b/base/libdl.jl index db8919c427ba7..b249038992ae9 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -86,13 +86,14 @@ else end const _dlname_cache = Dict{String, SubString{String}}() +const _dlname_cache_lock = ReentrantLock() """ dlname(fullpath::String) Returns the name of the library. """ -function dlname(fullpath::String)::SubString{String} +function _dlname_thread_unsafe(fullpath::String)::SubString{String} cache = get(_dlname_cache, fullpath, "") cache != "" && return cache bn = basename(fullpath) @@ -109,8 +110,10 @@ Check wheather the same shared library is loaded from two different files. The `warnings` determines if the warnings are printed to the console. """ function check_dllist() - fullpaths = dllist() - names = dlname.(fullpaths) + fullpaths, names = @lock _dlname_cache_lock begin + fullpaths = dllist() + fullpaths, _dlname_thread_unsafe.(fullpaths) + end perm = sortperm(names) dlabspath(x) = isfile(x) ? abspath(realpath(x)) : x for i in 1:length(names)-1 From a635c320c3344c97b5919f61b0044ef4b912f800 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Wed, 10 Nov 2021 16:04:58 +0100 Subject: [PATCH 08/14] Fix comment --- base/Base.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/Base.jl b/base/Base.jl index 8f8bf577f638a..cd8bbc5fc98fd 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -267,7 +267,7 @@ include("task.jl") include("threads_overloads.jl") include("weakkeydict.jl") -# Libdl +# Libc and Libdl include("libc.jl") using .Libc: getpid, gethostname, time From 527211ff2af5eab602a1871dbbe0e5fee309eb34 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Wed, 10 Nov 2021 16:21:04 +0100 Subject: [PATCH 09/14] Do not move sysinfo.jl in Base.jl --- base/Base.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index cd8bbc5fc98fd..2852eb729623d 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -250,13 +250,13 @@ include("missing.jl") # version include("version.jl") +# system & environment +include("sysinfo.jl") + # Logging include("logging.jl") using .CoreLogging -# system & environment -include("sysinfo.jl") - # Concurrency include("linked_list.jl") include("condition.jl") From 72a312b8d6fb4a5afa17ca45c1acda6a6fb9c8f6 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 11 Nov 2021 14:09:58 +0100 Subject: [PATCH 10/14] Use Dict instead of sortperm --- base/libdl.jl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index b249038992ae9..cedcb26c72834 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -85,24 +85,31 @@ else const dlpattern = r"^(.+?)\.so(?:\..*)?$" end -const _dlname_cache = Dict{String, SubString{String}}() +const _dlname_cache = Dict{String, String}() const _dlname_cache_lock = ReentrantLock() """ - dlname(fullpath::String) + _dlname_thread_unsafe(fullpath::String)::String Returns the name of the library. """ -function _dlname_thread_unsafe(fullpath::String)::SubString{String} +function _dlname_thread_unsafe(fullpath::String)::String cache = get(_dlname_cache, fullpath, "") cache != "" && return cache bn = basename(fullpath) m = match(dlpattern, bn) - ret = isnothing(m) ? bn : m.captures[1] + ret = String(isnothing(m) ? bn : m.captures[1]) _dlname_cache[fullpath] = ret return ret end +""" + _dlabspath(fullpath) + +Returns absolute path without symbolic links. +""" +_dlabspath(x) = isfile(x) ? abspath(realpath(x)) : x + """ check_dllist() @@ -114,18 +121,14 @@ function check_dllist() fullpaths = dllist() fullpaths, _dlname_thread_unsafe.(fullpaths) end - perm = sortperm(names) - dlabspath(x) = isfile(x) ? abspath(realpath(x)) : x - for i in 1:length(names)-1 - p1, p2 = perm[i], perm[i+1] - if names[p1] == names[p2] - if dlabspath(fullpaths[p1]) == dlabspath(fullpaths[p2]) - continue - end - @warn """Detected possible duplicate library loaded: $(names[p1]) + dict = Dict{String, String}() # name => path + for (name, path) in zip(names, fullpaths) + oldpath = get!(dict, name, path) + if path != oldpath && _dlabspath(path) != _dlabspath(oldpath) + @warn """Detected possible duplicate library loaded: $(name) This may lead to unexpected behavior! -$(fullpaths[p1]) -$(fullpaths[p2])""" maxlog=1 +$(path) +$(oldpath)""" maxlog=1 end end end From 9a694b8a19efa24a18d20af37196012f50214df4 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 11 Nov 2021 20:03:33 +0100 Subject: [PATCH 11/14] Do not export check_dllist() --- base/libdl.jl | 12 ++++++++---- stdlib/Libdl/src/Libdl.jl | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index cedcb26c72834..facb060e49e26 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -9,7 +9,7 @@ import Base.DL_LOAD_PATH export DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, - dlpath, find_library, dlext, dllist, check_dllist + dlpath, find_library, dlext, dllist """ DL_LOAD_PATH @@ -171,9 +171,13 @@ dlopen(s::Symbol, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; kwargs...) = dlopen(string(s), flags; kwargs...) function dlopen(s::AbstractString, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; throw_error::Bool = true, warnings::Bool = true) - ret = ccall(:jl_load_dynamic_library, Ptr{Cvoid}, (Cstring,UInt32,Cint), s, flags, Cint(throw_error)) - if ret == C_NULL - return nothing + if isempty(s) # Do not load anything, but run check_dllist() + ret = nothing + else + ret = ccall(:jl_load_dynamic_library, Ptr{Cvoid}, (Cstring,UInt32,Cint), s, flags, Cint(throw_error)) + if ret == C_NULL + return nothing + end end warnings && check_dllist() return ret diff --git a/stdlib/Libdl/src/Libdl.jl b/stdlib/Libdl/src/Libdl.jl index cbb026c2e00e4..df3f62c807fed 100644 --- a/stdlib/Libdl/src/Libdl.jl +++ b/stdlib/Libdl/src/Libdl.jl @@ -4,10 +4,10 @@ module Libdl # Just re-export Base.Libc.Libdl: export DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, - dlpath, find_library, dlext, dllist, check_dllist + dlpath, find_library, dlext, dllist import Base.Libc.Libdl: DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, - dlpath, find_library, dlext, dllist, check_dllist + dlpath, find_library, dlext, dllist end # module From 2b1a6c8452907a1f9d2bd3a35a3eb65cf73d76f0 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 11 Nov 2021 21:47:54 +0100 Subject: [PATCH 12/14] Rename fulpaths to paths --- base/libdl.jl | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index facb060e49e26..2596c9917c8d1 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -88,41 +88,25 @@ end const _dlname_cache = Dict{String, String}() const _dlname_cache_lock = ReentrantLock() -""" - _dlname_thread_unsafe(fullpath::String)::String - -Returns the name of the library. -""" -function _dlname_thread_unsafe(fullpath::String)::String - cache = get(_dlname_cache, fullpath, "") - cache != "" && return cache - bn = basename(fullpath) +# Returns the name of the library. +function _dlname(path) + bn = basename(path) m = match(dlpattern, bn) - ret = String(isnothing(m) ? bn : m.captures[1]) - _dlname_cache[fullpath] = ret - return ret + return isnothing(m) ? bn : m.captures[1] end -""" - _dlabspath(fullpath) - -Returns absolute path without symbolic links. -""" +# Returns absolute path without symbolic links. _dlabspath(x) = isfile(x) ? abspath(realpath(x)) : x -""" - check_dllist() - -Check wheather the same shared library is loaded from two different files. -The `warnings` determines if the warnings are printed to the console. -""" +# Checks if the same shared library is loaded from two different files. function check_dllist() - fullpaths, names = @lock _dlname_cache_lock begin - fullpaths = dllist() - fullpaths, _dlname_thread_unsafe.(fullpaths) + paths, names = @lock _dlname_cache_lock begin + paths = dllist() + names = [get!(() -> _dlname(x), _dlname_cache, x) for x in paths] + paths, names end dict = Dict{String, String}() # name => path - for (name, path) in zip(names, fullpaths) + for (name, path) in zip(names, paths) oldpath = get!(dict, name, path) if path != oldpath && _dlabspath(path) != _dlabspath(oldpath) @warn """Detected possible duplicate library loaded: $(name) From aa253459680ad6cbaa6326e18336f413d52a28ee Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 11 Nov 2021 23:35:59 +0100 Subject: [PATCH 13/14] Add option to suppress warnings --- base/libdl.jl | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index 2596c9917c8d1..588854e7634cd 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -85,9 +85,6 @@ else const dlpattern = r"^(.+?)\.so(?:\..*)?$" end -const _dlname_cache = Dict{String, String}() -const _dlname_cache_lock = ReentrantLock() - # Returns the name of the library. function _dlname(path) bn = basename(path) @@ -98,21 +95,24 @@ end # Returns absolute path without symbolic links. _dlabspath(x) = isfile(x) ? abspath(realpath(x)) : x +const _dlname_cache = Dict{String, String}() +const _suppressed_warnings = Set{String}() + # Checks if the same shared library is loaded from two different files. -function check_dllist() - paths, names = @lock _dlname_cache_lock begin - paths = dllist() - names = [get!(() -> _dlname(x), _dlname_cache, x) for x in paths] - paths, names - end +function _check_dllist() + paths = dllist() + names = [get!(() -> _dlname(x), _dlname_cache, x) for x in paths] dict = Dict{String, String}() # name => path for (name, path) in zip(names, paths) oldpath = get!(dict, name, path) if path != oldpath && _dlabspath(path) != _dlabspath(oldpath) + name ∈ _suppressed_warnings && continue @warn """Detected possible duplicate library loaded: $(name) This may lead to unexpected behavior! $(path) -$(oldpath)""" maxlog=1 +$(oldpath) +To suppress this warning, you can use the following argument: +dlopen([name_or_path]; suppress_warnings = ["$(name)"])""" maxlog=1 end end end @@ -151,20 +151,25 @@ If the library cannot be found, this method throws an error, unless the keyword """ function dlopen end +const _dlopen_lock = ReentrantLock() + dlopen(s::Symbol, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; kwargs...) = dlopen(string(s), flags; kwargs...) -function dlopen(s::AbstractString, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; throw_error::Bool = true, warnings::Bool = true) - if isempty(s) # Do not load anything, but run check_dllist() - ret = nothing - else +function dlopen(s::AbstractString, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; throw_error::Bool = true, suppress_warnings::Vector{String} = String[]) + @lock _dlopen_lock begin + union!(_suppressed_warnings, suppress_warnings) + if isempty(s) # Do not load anything, but run _check_dllist() + _check_dllist() + return nothing + end ret = ccall(:jl_load_dynamic_library, Ptr{Cvoid}, (Cstring,UInt32,Cint), s, flags, Cint(throw_error)) if ret == C_NULL return nothing end + _check_dllist() + return ret end - warnings && check_dllist() - return ret end """ From 46a9ba0e0029d3a9e3780912ed2e9547ca1313b4 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Mon, 15 Nov 2021 13:04:53 +0100 Subject: [PATCH 14/14] Add comments and note to docs --- base/libdl.jl | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index 588854e7634cd..d0f3693f2737f 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -104,21 +104,26 @@ function _check_dllist() names = [get!(() -> _dlname(x), _dlname_cache, x) for x in paths] dict = Dict{String, String}() # name => path for (name, path) in zip(names, paths) + # Test if there is a duplicity (name already in `dict`) oldpath = get!(dict, name, path) - if path != oldpath && _dlabspath(path) != _dlabspath(oldpath) - name ∈ _suppressed_warnings && continue - @warn """Detected possible duplicate library loaded: $(name) + path == oldpath && continue + # Test if already suppressed + name ∈ _suppressed_warnings && continue + # Show the warning only once (suppress in the future) + push!(_suppressed_warnings, name) + # Test if absolute paths are equal + _dlabspath(path) == _dlabspath(oldpath) && continue + @warn """Detected possible duplicate library loaded: $(name) This may lead to unexpected behavior! $(path) $(oldpath) To suppress this warning, you can use the following argument: -dlopen([name_or_path]; suppress_warnings = ["$(name)"])""" maxlog=1 - end +dlopen([name_or_path]; suppress_warnings = ["$(name)"])""" end end """ - dlopen(libfile::AbstractString [, flags::Integer]; throw_error:Bool = true) + dlopen(libfile::AbstractString [, flags::Integer]; throw_error:Bool = true, suppress_warnings::Vector{String} = String[]) Load a shared library, returning an opaque handle. @@ -148,6 +153,12 @@ If the library cannot be found, this method throws an error, unless the keyword From Julia 1.6 on, this method replaces paths starting with `@executable_path/` with the path to the Julia executable, allowing for relocatable relative-path loads. In Julia 1.5 and earlier, this only worked on macOS. + +!!! note + From Julia 1.8 on, this method detects when a library with the same name is loaded + multiple times from different files because it may lead to unexpected behavior. In such + a case, the method produces a warning. The warning be suppressed by adding the name of + the library into a newly introduced keyword argument `suppress_warnings`. """ function dlopen end