From a6aafd0b5cdd05bce97e5a64c49cd9f9459f19a1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 5 Aug 2025 12:22:40 -0400 Subject: [PATCH 1/2] Revert back to the standard form for ccall which supports LazyLibrary improvements We had initially planned a breaking change design for LazyLibrary, which this package was updated for, but later we realized a better design which allows going back to the original version of this code. The previous form is still very poorly supported in the compiler and was still rather buggy internally. Revert "Replace `__init__` with `OncePerProcess` on supported Julia versions (#316)" This reverts commit dbcb5d210be95a0edac920646884784da87f7cab. Revert "Make FFTW relocatable for PackageCompiler" This reverts commit e96270a112ff2c16e19fc6aaac1ec429668ebe2f. --- Project.toml | 2 ++ src/FFTW.jl | 82 ++++++++++++++++++++++++++++++----------------- src/fft.jl | 83 +++++++++++++++++++++++++----------------------- src/providers.jl | 22 +++---------- test/runtests.jl | 2 +- 5 files changed, 102 insertions(+), 89 deletions(-) diff --git a/Project.toml b/Project.toml index 4840745..836c3d7 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "1.10.0-dev" [deps] AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c" FFTW_jll = "f5851436-0d7a-5f13-b9de-f02708fd171a" +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7" Preferences = "21216c6a-2e73-6563-6e65-726566657250" @@ -14,6 +15,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69" AbstractFFTs = "1.5" Aqua = "0.8" FFTW_jll = "3.3.9" +Libdl = "<0.0.1, 1.6" LinearAlgebra = "<0.0.1, 1" MKL_jll = "2019.0.117, 2020, 2021, 2022, 2023, 2024, 2025" Preferences = "1.2" diff --git a/src/FFTW.jl b/src/FFTW.jl index 1a9352f..5c050dd 100644 --- a/src/FFTW.jl +++ b/src/FFTW.jl @@ -16,48 +16,70 @@ export dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct! include("providers.jl") -function initialize_library_paths() - # If someone is trying to set the provider via the old environment variable, warn them that they - # should instead use `set_provider!()` instead. +function check_env() if haskey(ENV, "JULIA_FFTW_PROVIDER") Base.depwarn("JULIA_FFTW_PROVIDER is deprecated; use FFTW.set_provider!() instead", :JULIA_FFTW_PROVIDER) end +end - # Hook FFTW threads up to our partr runtime, and re-assign the - # libfftw3{,f} refs at runtime, since we may have relocated and - # changed the path to the library since the last time we precompiled. +if VERSION >= v"1.11.0" +# This can be be deleted once FFTW_jll is upgraded to the real lazy jll code, to get the real benefits of this mess +mutable struct FakeLazyLibrary + reallibrary::Symbol + on_load_callback + @atomic h::Ptr{Cvoid} +end +import Libdl: LazyLibrary, dlopen +function dlopen(lib::FakeLazyLibrary) + h = @atomic :monotonic lib.h + h != C_NULL && return h + @lock fftwlock begin + h = @atomic :monotonic lib.h + h != C_NULL && return h + h = dlopen(getglobal(FFTW, lib.reallibrary)) + lib.on_load_callback() + @atomic :release lib.h = h + end + return h +end +function fftw_init_check() + check_env() @static if fftw_provider == "fftw" - libfftw3_path[] = FFTW_jll.libfftw3_path - libfftw3f_path[] = FFTW_jll.libfftw3f_path fftw_init_threads() end - @static if fftw_provider == "mkl" - libfftw3_path[] = MKL_jll.libmkl_rt_path - libfftw3f_path[] = MKL_jll.libmkl_rt_path - end - return nothing end -if VERSION >= v"1.12.0-beta1.29" - const initialize_library_paths_once = OncePerProcess{Nothing}() do - initialize_library_paths() - return - end - function libfftw3() - initialize_library_paths_once() - return libfftw3_path[] - end - function libfftw3f() - initialize_library_paths_once() - return libfftw3f_path[] - end +@static if fftw_provider == "fftw" + import FFTW_jll: libfftw3 as libfftw3_no_init, + libfftw3f as libfftw3f_no_init +elseif fftw_provider == "mkl" + import MKL_jll: libmkl_rt as libfftw3_no_init, + libmkl_rt as libfftw3f_no_init +end +const libfftw3 = FakeLazyLibrary(:libfftw3_no_init, fftw_init_check, C_NULL) +const libfftw3f = FakeLazyLibrary(:libfftw3f_no_init, fftw_init_check, C_NULL) + else - function __init__() - initialize_library_paths() +function __init__() + # If someone is trying to set the provider via the old environment variable, warn them that they + # should instead use `set_provider!()` instead. + check_env() + + global libfftw3 + global libfftw3f + # Hook FFTW threads up to our partr runtime + @static if fftw_provider == "fftw" + libfftw3 = FFTW_jll.libfftw3_path + libfftw3f = FFTW_jll.libfftw3f_path + fftw_init_threads() + end + @static if fftw_provider == "mkl" + libfftw3 = MKL_jll.libmkl_rt_path + libfftw3f = MKL_jll.libmkl_rt_path end - libfftw3() = libfftw3_path[] - libfftw3f() = libfftw3f_path[] end +end + # most FFTW calls other than fftw_execute should be protected by a lock to be thread-safe const fftwlock = ReentrantLock() diff --git a/src/fft.jl b/src/fft.jl index 6b8a017..7c13f59 100644 --- a/src/fft.jl +++ b/src/fft.jl @@ -55,8 +55,11 @@ function plan_r2r end ## FFT: Implement fft by calling fftw. -const version = VersionNumber(split(unsafe_string(cglobal( - (:fftw_version,libfftw3()), UInt8)), ['-', ' '])[2]) +# TODO: this is dangerous since it captures runtime data from the compile machine +# n.b. write this as a function to avoid Julia bugs with the runtime cglobal implementation +get_version() = VersionNumber(split(unsafe_string(cglobal( + (:fftw_version,libfftw3_no_init), UInt8)), ['-', ' '])[2]) +const version = get_version() ## Direction of FFT @@ -141,32 +144,32 @@ alignment_of(A::FakeArray) = Int32(0) @exclusive function export_wisdom(fname::AbstractString) f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :w) systemerror("could not open wisdom file $fname for writing", f == C_NULL) - ccall((:fftw_export_wisdom_to_file,libfftw3()), Cvoid, (Ptr{Cvoid},), f) + ccall((:fftw_export_wisdom_to_file,libfftw3), Cvoid, (Ptr{Cvoid},), f) ccall(:fputs, Int32, (Ptr{UInt8},Ptr{Cvoid}), " "^256, f) # no NUL, hence no Cstring - ccall((:fftwf_export_wisdom_to_file,libfftw3f()), Cvoid, (Ptr{Cvoid},), f) + ccall((:fftwf_export_wisdom_to_file,libfftw3f), Cvoid, (Ptr{Cvoid},), f) ccall(:fclose, Cvoid, (Ptr{Cvoid},), f) end @exclusive function import_wisdom(fname::AbstractString) f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :r) systemerror("could not open wisdom file $fname for reading", f == C_NULL) - if ccall((:fftw_import_wisdom_from_file,libfftw3()),Int32,(Ptr{Cvoid},),f)==0|| - ccall((:fftwf_import_wisdom_from_file,libfftw3f()),Int32,(Ptr{Cvoid},),f)==0 + if ccall((:fftw_import_wisdom_from_file,libfftw3),Int32,(Ptr{Cvoid},),f)==0|| + ccall((:fftwf_import_wisdom_from_file,libfftw3f),Int32,(Ptr{Cvoid},),f)==0 error("failed to import wisdom from $fname") end ccall(:fclose, Cvoid, (Ptr{Cvoid},), f) end @exclusive function import_system_wisdom() - if ccall((:fftw_import_system_wisdom,libfftw3()), Int32, ()) == 0 || - ccall((:fftwf_import_system_wisdom,libfftw3f()), Int32, ()) == 0 + if ccall((:fftw_import_system_wisdom,libfftw3), Int32, ()) == 0 || + ccall((:fftwf_import_system_wisdom,libfftw3f), Int32, ()) == 0 error("failed to import system wisdom") end end @exclusive function forget_wisdom() - ccall((:fftw_forget_wisdom,libfftw3()), Cvoid, ()) - ccall((:fftwf_forget_wisdom,libfftw3f()), Cvoid, ()) + ccall((:fftw_forget_wisdom,libfftw3), Cvoid, ()) + ccall((:fftwf_forget_wisdom,libfftw3f), Cvoid, ()) end # Threads @@ -176,15 +179,15 @@ function _set_num_threads(num_threads::Integer) @static if fftw_provider == "mkl" _last_num_threads[] = num_threads end - ccall((:fftw_plan_with_nthreads,libfftw3()), Cvoid, (Int32,), num_threads) - ccall((:fftwf_plan_with_nthreads,libfftw3f()), Cvoid, (Int32,), num_threads) + ccall((:fftw_plan_with_nthreads,libfftw3), Cvoid, (Int32,), num_threads) + ccall((:fftwf_plan_with_nthreads,libfftw3f), Cvoid, (Int32,), num_threads) end @exclusive set_num_threads(num_threads::Integer) = _set_num_threads(num_threads) function get_num_threads() @static if fftw_provider == "fftw" - ccall((:fftw_planner_nthreads,libfftw3()), Cint, ()) + ccall((:fftw_planner_nthreads,libfftw3), Cint, ()) else _last_num_threads[] end @@ -211,9 +214,9 @@ const NO_TIMELIMIT = -1.0 # from fftw3.h # only call these when fftwlock is held: unsafe_set_timelimit(precision::fftwTypeDouble,seconds) = - ccall((:fftw_set_timelimit,libfftw3()), Cvoid, (Float64,), seconds) + ccall((:fftw_set_timelimit,libfftw3), Cvoid, (Float64,), seconds) unsafe_set_timelimit(precision::fftwTypeSingle,seconds) = - ccall((:fftwf_set_timelimit,libfftw3f()), Cvoid, (Float64,), seconds) + ccall((:fftwf_set_timelimit,libfftw3f), Cvoid, (Float64,), seconds) @exclusive set_timelimit(precision, seconds) = unsafe_set_timelimit(precision, seconds) # Array alignment mod 16: @@ -234,9 +237,9 @@ unsafe_set_timelimit(precision::fftwTypeSingle,seconds) = convert(Int32, convert(Int64, pointer(A)) % 16) else alignment_of(A::StridedArray{T}) where {T<:fftwDouble} = - ccall((:fftw_alignment_of, libfftw3()), Int32, (Ptr{T},), A) + ccall((:fftw_alignment_of, libfftw3), Int32, (Ptr{T},), A) alignment_of(A::StridedArray{T}) where {T<:fftwSingle} = - ccall((:fftwf_alignment_of, libfftw3f()), Int32, (Ptr{T},), A) + ccall((:fftwf_alignment_of, libfftw3f), Int32, (Ptr{T},), A) end # FFTWPlan (low-level) @@ -320,9 +323,9 @@ unsafe_convert(::Type{PlanPtr}, p::FFTWPlan) = p.plan # these functions should only be called while the fftwlock is held unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwDouble})) = - ccall((:fftw_destroy_plan,libfftw3()), Cvoid, (PlanPtr,), plan) + ccall((:fftw_destroy_plan,libfftw3), Cvoid, (PlanPtr,), plan) unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwSingle})) = - ccall((:fftwf_destroy_plan,libfftw3f()), Cvoid, (PlanPtr,), plan) + ccall((:fftwf_destroy_plan,libfftw3f), Cvoid, (PlanPtr,), plan) const deferred_destroy_lock = ReentrantLock() # lock protecting the deferred_destroy_plans list const deferred_destroy_plans = FFTWPlan[] @@ -383,19 +386,19 @@ end ################################################################################################# cost(plan::FFTWPlan{<:fftwDouble}) = - ccall((:fftw_cost,libfftw3()), Float64, (PlanPtr,), plan) + ccall((:fftw_cost,libfftw3), Float64, (PlanPtr,), plan) cost(plan::FFTWPlan{<:fftwSingle}) = - ccall((:fftwf_cost,libfftw3f()), Float64, (PlanPtr,), plan) + ccall((:fftwf_cost,libfftw3f), Float64, (PlanPtr,), plan) @exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwDouble}) add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0) - ccall((:fftw_flops,libfftw3()), Cvoid, + ccall((:fftw_flops,libfftw3), Cvoid, (PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma) return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[])) end @exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwSingle}) add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0) - ccall((:fftwf_flops,libfftw3f()), Cvoid, + ccall((:fftwf_flops,libfftw3f), Cvoid, (PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma) return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[])) end @@ -426,9 +429,9 @@ const has_sprint_plan = version >= v"3.3.4" && fftw_provider == "fftw" @static if has_sprint_plan sprint_plan_(plan::FFTWPlan{<:fftwDouble}) = - ccall((:fftw_sprint_plan,libfftw3()), Ptr{UInt8}, (PlanPtr,), plan) + ccall((:fftw_sprint_plan,libfftw3), Ptr{UInt8}, (PlanPtr,), plan) sprint_plan_(plan::FFTWPlan{<:fftwSingle}) = - ccall((:fftwf_sprint_plan,libfftw3f()), Ptr{UInt8}, (PlanPtr,), plan) + ccall((:fftwf_sprint_plan,libfftw3f), Ptr{UInt8}, (PlanPtr,), plan) function sprint_plan(plan::FFTWPlan) p = sprint_plan_(plan) str = unsafe_string(p) @@ -510,49 +513,49 @@ _colmajorstrides(p) = () # Execute unsafe_execute!(plan::FFTWPlan{<:fftwDouble}) = - ccall((:fftw_execute,libfftw3()), Cvoid, (PlanPtr,), plan) + ccall((:fftw_execute,libfftw3), Cvoid, (PlanPtr,), plan) unsafe_execute!(plan::FFTWPlan{<:fftwSingle}) = - ccall((:fftwf_execute,libfftw3f()), Cvoid, (PlanPtr,), plan) + ccall((:fftwf_execute,libfftw3f), Cvoid, (PlanPtr,), plan) unsafe_execute!(plan::cFFTWPlan{T}, X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} = - ccall((:fftw_execute_dft,libfftw3()), Cvoid, + ccall((:fftw_execute_dft,libfftw3), Cvoid, (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) unsafe_execute!(plan::cFFTWPlan{T}, X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} = - ccall((:fftwf_execute_dft,libfftw3f()), Cvoid, + ccall((:fftwf_execute_dft,libfftw3f), Cvoid, (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) unsafe_execute!(plan::rFFTWPlan{Float64,FORWARD}, X::StridedArray{Float64}, Y::StridedArray{Complex{Float64}}) = - ccall((:fftw_execute_dft_r2c,libfftw3()), Cvoid, + ccall((:fftw_execute_dft_r2c,libfftw3), Cvoid, (PlanPtr,Ptr{Float64},Ptr{Complex{Float64}}), plan, X, Y) unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD}, X::StridedArray{Float32}, Y::StridedArray{Complex{Float32}}) = - ccall((:fftwf_execute_dft_r2c,libfftw3f()), Cvoid, + ccall((:fftwf_execute_dft_r2c,libfftw3f), Cvoid, (PlanPtr,Ptr{Float32},Ptr{Complex{Float32}}), plan, X, Y) unsafe_execute!(plan::rFFTWPlan{Complex{Float64},BACKWARD}, X::StridedArray{Complex{Float64}}, Y::StridedArray{Float64}) = - ccall((:fftw_execute_dft_c2r,libfftw3()), Cvoid, + ccall((:fftw_execute_dft_c2r,libfftw3), Cvoid, (PlanPtr,Ptr{Complex{Float64}},Ptr{Float64}), plan, X, Y) unsafe_execute!(plan::rFFTWPlan{Complex{Float32},BACKWARD}, X::StridedArray{Complex{Float32}}, Y::StridedArray{Float32}) = - ccall((:fftwf_execute_dft_c2r,libfftw3f()), Cvoid, + ccall((:fftwf_execute_dft_c2r,libfftw3f), Cvoid, (PlanPtr,Ptr{Complex{Float32}},Ptr{Float32}), plan, X, Y) unsafe_execute!(plan::r2rFFTWPlan{T}, X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} = - ccall((:fftw_execute_r2r,libfftw3()), Cvoid, + ccall((:fftw_execute_r2r,libfftw3), Cvoid, (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) unsafe_execute!(plan::r2rFFTWPlan{T}, X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} = - ccall((:fftwf_execute_r2r,libfftw3f()), Cvoid, + ccall((:fftwf_execute_r2r,libfftw3f), Cvoid, (PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) # NOTE ON GC (garbage collection): @@ -649,7 +652,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3), unsafe_set_timelimit($Tr, timelimit) R = isa(region, Tuple) ? region : copy(region) dims, howmany = dims_howmany(X, Y, size(X), R) - plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib()), + plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib), PlanPtr, (Int32, Ptr{Int}, Int32, Ptr{Int}, Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32), @@ -669,7 +672,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3), regionshft = _circshiftmin1(region) # FFTW halves last dim unsafe_set_timelimit($Tr, timelimit) dims, howmany = dims_howmany(X, Y, size(X), regionshft) - plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib()), + plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib), PlanPtr, (Int32, Ptr{Int}, Int32, Ptr{Int}, Ptr{$Tr}, Ptr{$Tc}, UInt32), @@ -689,7 +692,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3), regionshft = _circshiftmin1(region) # FFTW halves last dim unsafe_set_timelimit($Tr, timelimit) dims, howmany = dims_howmany(X, Y, size(Y), regionshft) - plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib()), + plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib), PlanPtr, (Int32, Ptr{Int}, Int32, Ptr{Int}, Ptr{$Tc}, Ptr{$Tr}, UInt32), @@ -711,7 +714,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3), knd = fix_kinds(region, kinds) unsafe_set_timelimit($Tr, timelimit) dims, howmany = dims_howmany(X, Y, size(X), region) - plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()), + plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib), PlanPtr, (Int32, Ptr{Int}, Int32, Ptr{Int}, Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32), @@ -739,7 +742,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3), howmany[2:3, :] .*= 2 end howmany = [howmany [2,1,1]] # append loop over real/imag parts - plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()), + plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib), PlanPtr, (Int32, Ptr{Int}, Int32, Ptr{Int}, Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32), diff --git a/src/providers.jl b/src/providers.jl index 826ab48..6764fd2 100644 --- a/src/providers.jl +++ b/src/providers.jl @@ -20,12 +20,6 @@ end # Read in preferences, see if any users have requested a particular backend const fftw_provider = get_provider() -# We'll initialize `libfftw3` here (in the conditionals below), and -# it will get overwritten again in `__init__()`. This allows us to -# `ccall` at build time, and also be relocatable for PackageCompiler. -const libfftw3_path = Ref{String}() -const libfftw3f_path = Ref{String}() - """ set_provider!(provider; export_prefs::Bool = false) @@ -48,9 +42,6 @@ end # If we're using fftw_jll, load it in @static if fftw_provider == "fftw" import FFTW_jll - libfftw3_path[] = FFTW_jll.libfftw3_path - libfftw3f_path[] = FFTW_jll.libfftw3f_path - # callback function that FFTW uses to launch `num` parallel # tasks (FFTW/fftw3#175): function spawnloop(f::Ptr{Cvoid}, fdata::Ptr{Cvoid}, elsize::Csize_t, num::Cint, callback_data::Ptr{Cvoid}) @@ -66,19 +57,16 @@ end # (Previously, we called fftw_cleanup, but this invalidated existing # plans, causing Base Julia issue #19892.) function fftw_init_threads() - # We de-reference libfftw3(f)_path directly in this function instead of using - # `libfftw3(f)()` to avoid the circular dependency and thus a stack overflow. This - # function is only called after the path references are initialized. - stat = ccall((:fftw_init_threads, libfftw3_path[]), Int32, ()) - statf = ccall((:fftwf_init_threads, libfftw3f_path[]), Int32, ()) + stat = ccall((:fftw_init_threads, libfftw3_no_init), Int32, ()) + statf = ccall((:fftwf_init_threads, libfftw3f_no_init), Int32, ()) if stat == 0 || statf == 0 error("could not initialize FFTW threads") end if nthreads() > 1 cspawnloop = @cfunction(spawnloop, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Cint, Ptr{Cvoid})) - ccall((:fftw_threads_set_callback, libfftw3_path[]), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL) - ccall((:fftwf_threads_set_callback, libfftw3f_path[]), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL) + ccall((:fftw_threads_set_callback, libfftw3_no_init), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL) + ccall((:fftwf_threads_set_callback, libfftw3f_no_init), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL) end end end @@ -86,7 +74,5 @@ end # If we're using MKL, load it in and set library paths appropriately. @static if fftw_provider == "mkl" import MKL_jll - libfftw3_path[] = MKL_jll.libmkl_rt_path - libfftw3f_path[] = MKL_jll.libmkl_rt_path const _last_num_threads = Ref(Cint(1)) end diff --git a/test/runtests.jl b/test/runtests.jl index 1a3eba5..d84eb50 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -508,7 +508,7 @@ end # fftw_provider == "fftw" end # check whether FFTW on this architecture has nontrivial alignment requirements - nontrivial_alignment = FFTW.fftw_provider == "fftw" && ccall((:fftwf_alignment_of, FFTW.libfftw3f()), Int32, (Int,), 8) != 0 + nontrivial_alignment = FFTW.fftw_provider == "fftw" && ccall((:fftwf_alignment_of, FFTW.libfftw3f), Int32, (Int,), 8) != 0 if nontrivial_alignment @test_throws ArgumentError plan_rfft(Array{Float32}(undef, 32)) * view(A, 2:33) @test_throws ArgumentError plan_fft(Array{Complex{Float32}}(undef, 32)) * view(Ac, 2:33) From a14e1fb043711de01f6b021571f89cf36033dafa Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 20 Aug 2025 16:23:52 +0000 Subject: [PATCH 2/2] fixup! Revert back to the standard form for ccall which supports LazyLibrary improvements --- src/FFTW.jl | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/FFTW.jl b/src/FFTW.jl index 5c050dd..8884704 100644 --- a/src/FFTW.jl +++ b/src/FFTW.jl @@ -16,10 +16,17 @@ export dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct! include("providers.jl") -function check_env() +function fftw_init_check() + # If someone is trying to set the provider via the old environment variable, warn them that they + # should instead use `set_provider!()` instead. if haskey(ENV, "JULIA_FFTW_PROVIDER") Base.depwarn("JULIA_FFTW_PROVIDER is deprecated; use FFTW.set_provider!() instead", :JULIA_FFTW_PROVIDER) end + + # Hook FFTW threads up to our partr runtime + @static if fftw_provider == "fftw" + fftw_init_threads() + end end if VERSION >= v"1.11.0" @@ -42,12 +49,6 @@ function dlopen(lib::FakeLazyLibrary) end return h end -function fftw_init_check() - check_env() - @static if fftw_provider == "fftw" - fftw_init_threads() - end -end @static if fftw_provider == "fftw" import FFTW_jll: libfftw3 as libfftw3_no_init, @@ -60,23 +61,19 @@ const libfftw3 = FakeLazyLibrary(:libfftw3_no_init, fftw_init_check, C_NULL) const libfftw3f = FakeLazyLibrary(:libfftw3f_no_init, fftw_init_check, C_NULL) else +@static if fftw_provider == "fftw" + import FFTW_jll: libfftw3_path as libfftw3_no_init, + libfftw3f_path as libfftw3f_no_init, + libfftw3_path as libfftw3, + libfftw3f_path as libfftw3f +elseif fftw_provider == "mkl" + import MKL_jll: libmkl_rt_path as libfftw3_no_init, + libmkl_rt_path as libfftw3f_no_init, + libmkl_rt_path as libfftw3, + libmkl_rt_path as libfftw3f +end function __init__() - # If someone is trying to set the provider via the old environment variable, warn them that they - # should instead use `set_provider!()` instead. - check_env() - - global libfftw3 - global libfftw3f - # Hook FFTW threads up to our partr runtime - @static if fftw_provider == "fftw" - libfftw3 = FFTW_jll.libfftw3_path - libfftw3f = FFTW_jll.libfftw3f_path - fftw_init_threads() - end - @static if fftw_provider == "mkl" - libfftw3 = MKL_jll.libmkl_rt_path - libfftw3f = MKL_jll.libmkl_rt_path - end + fftw_init_check() end end