Skip to content

Commit a6aafd0

Browse files
committed
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 dbcb5d2. Revert "Make FFTW relocatable for PackageCompiler" This reverts commit e96270a.
1 parent 7d0f38c commit a6aafd0

File tree

5 files changed

+102
-89
lines changed

5 files changed

+102
-89
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ version = "1.10.0-dev"
55
[deps]
66
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"
77
FFTW_jll = "f5851436-0d7a-5f13-b9de-f02708fd171a"
8+
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
89
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
910
MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
1011
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
@@ -14,6 +15,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1415
AbstractFFTs = "1.5"
1516
Aqua = "0.8"
1617
FFTW_jll = "3.3.9"
18+
Libdl = "<0.0.1, 1.6"
1719
LinearAlgebra = "<0.0.1, 1"
1820
MKL_jll = "2019.0.117, 2020, 2021, 2022, 2023, 2024, 2025"
1921
Preferences = "1.2"

src/FFTW.jl

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,70 @@ export dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct!
1616

1717
include("providers.jl")
1818

19-
function initialize_library_paths()
20-
# If someone is trying to set the provider via the old environment variable, warn them that they
21-
# should instead use `set_provider!()` instead.
19+
function check_env()
2220
if haskey(ENV, "JULIA_FFTW_PROVIDER")
2321
Base.depwarn("JULIA_FFTW_PROVIDER is deprecated; use FFTW.set_provider!() instead", :JULIA_FFTW_PROVIDER)
2422
end
23+
end
2524

26-
# Hook FFTW threads up to our partr runtime, and re-assign the
27-
# libfftw3{,f} refs at runtime, since we may have relocated and
28-
# changed the path to the library since the last time we precompiled.
25+
if VERSION >= v"1.11.0"
26+
# This can be be deleted once FFTW_jll is upgraded to the real lazy jll code, to get the real benefits of this mess
27+
mutable struct FakeLazyLibrary
28+
reallibrary::Symbol
29+
on_load_callback
30+
@atomic h::Ptr{Cvoid}
31+
end
32+
import Libdl: LazyLibrary, dlopen
33+
function dlopen(lib::FakeLazyLibrary)
34+
h = @atomic :monotonic lib.h
35+
h != C_NULL && return h
36+
@lock fftwlock begin
37+
h = @atomic :monotonic lib.h
38+
h != C_NULL && return h
39+
h = dlopen(getglobal(FFTW, lib.reallibrary))
40+
lib.on_load_callback()
41+
@atomic :release lib.h = h
42+
end
43+
return h
44+
end
45+
function fftw_init_check()
46+
check_env()
2947
@static if fftw_provider == "fftw"
30-
libfftw3_path[] = FFTW_jll.libfftw3_path
31-
libfftw3f_path[] = FFTW_jll.libfftw3f_path
3248
fftw_init_threads()
3349
end
34-
@static if fftw_provider == "mkl"
35-
libfftw3_path[] = MKL_jll.libmkl_rt_path
36-
libfftw3f_path[] = MKL_jll.libmkl_rt_path
37-
end
38-
return nothing
3950
end
4051

41-
if VERSION >= v"1.12.0-beta1.29"
42-
const initialize_library_paths_once = OncePerProcess{Nothing}() do
43-
initialize_library_paths()
44-
return
45-
end
46-
function libfftw3()
47-
initialize_library_paths_once()
48-
return libfftw3_path[]
49-
end
50-
function libfftw3f()
51-
initialize_library_paths_once()
52-
return libfftw3f_path[]
53-
end
52+
@static if fftw_provider == "fftw"
53+
import FFTW_jll: libfftw3 as libfftw3_no_init,
54+
libfftw3f as libfftw3f_no_init
55+
elseif fftw_provider == "mkl"
56+
import MKL_jll: libmkl_rt as libfftw3_no_init,
57+
libmkl_rt as libfftw3f_no_init
58+
end
59+
const libfftw3 = FakeLazyLibrary(:libfftw3_no_init, fftw_init_check, C_NULL)
60+
const libfftw3f = FakeLazyLibrary(:libfftw3f_no_init, fftw_init_check, C_NULL)
61+
5462
else
55-
function __init__()
56-
initialize_library_paths()
63+
function __init__()
64+
# If someone is trying to set the provider via the old environment variable, warn them that they
65+
# should instead use `set_provider!()` instead.
66+
check_env()
67+
68+
global libfftw3
69+
global libfftw3f
70+
# Hook FFTW threads up to our partr runtime
71+
@static if fftw_provider == "fftw"
72+
libfftw3 = FFTW_jll.libfftw3_path
73+
libfftw3f = FFTW_jll.libfftw3f_path
74+
fftw_init_threads()
75+
end
76+
@static if fftw_provider == "mkl"
77+
libfftw3 = MKL_jll.libmkl_rt_path
78+
libfftw3f = MKL_jll.libmkl_rt_path
5779
end
58-
libfftw3() = libfftw3_path[]
59-
libfftw3f() = libfftw3f_path[]
6080
end
81+
end
82+
6183

6284
# most FFTW calls other than fftw_execute should be protected by a lock to be thread-safe
6385
const fftwlock = ReentrantLock()

src/fft.jl

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@ function plan_r2r end
5555

5656
## FFT: Implement fft by calling fftw.
5757

58-
const version = VersionNumber(split(unsafe_string(cglobal(
59-
(:fftw_version,libfftw3()), UInt8)), ['-', ' '])[2])
58+
# TODO: this is dangerous since it captures runtime data from the compile machine
59+
# n.b. write this as a function to avoid Julia bugs with the runtime cglobal implementation
60+
get_version() = VersionNumber(split(unsafe_string(cglobal(
61+
(:fftw_version,libfftw3_no_init), UInt8)), ['-', ' '])[2])
62+
const version = get_version()
6063

6164
## Direction of FFT
6265

@@ -141,32 +144,32 @@ alignment_of(A::FakeArray) = Int32(0)
141144
@exclusive function export_wisdom(fname::AbstractString)
142145
f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :w)
143146
systemerror("could not open wisdom file $fname for writing", f == C_NULL)
144-
ccall((:fftw_export_wisdom_to_file,libfftw3()), Cvoid, (Ptr{Cvoid},), f)
147+
ccall((:fftw_export_wisdom_to_file,libfftw3), Cvoid, (Ptr{Cvoid},), f)
145148
ccall(:fputs, Int32, (Ptr{UInt8},Ptr{Cvoid}), " "^256, f) # no NUL, hence no Cstring
146-
ccall((:fftwf_export_wisdom_to_file,libfftw3f()), Cvoid, (Ptr{Cvoid},), f)
149+
ccall((:fftwf_export_wisdom_to_file,libfftw3f), Cvoid, (Ptr{Cvoid},), f)
147150
ccall(:fclose, Cvoid, (Ptr{Cvoid},), f)
148151
end
149152

150153
@exclusive function import_wisdom(fname::AbstractString)
151154
f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :r)
152155
systemerror("could not open wisdom file $fname for reading", f == C_NULL)
153-
if ccall((:fftw_import_wisdom_from_file,libfftw3()),Int32,(Ptr{Cvoid},),f)==0||
154-
ccall((:fftwf_import_wisdom_from_file,libfftw3f()),Int32,(Ptr{Cvoid},),f)==0
156+
if ccall((:fftw_import_wisdom_from_file,libfftw3),Int32,(Ptr{Cvoid},),f)==0||
157+
ccall((:fftwf_import_wisdom_from_file,libfftw3f),Int32,(Ptr{Cvoid},),f)==0
155158
error("failed to import wisdom from $fname")
156159
end
157160
ccall(:fclose, Cvoid, (Ptr{Cvoid},), f)
158161
end
159162

160163
@exclusive function import_system_wisdom()
161-
if ccall((:fftw_import_system_wisdom,libfftw3()), Int32, ()) == 0 ||
162-
ccall((:fftwf_import_system_wisdom,libfftw3f()), Int32, ()) == 0
164+
if ccall((:fftw_import_system_wisdom,libfftw3), Int32, ()) == 0 ||
165+
ccall((:fftwf_import_system_wisdom,libfftw3f), Int32, ()) == 0
163166
error("failed to import system wisdom")
164167
end
165168
end
166169

167170
@exclusive function forget_wisdom()
168-
ccall((:fftw_forget_wisdom,libfftw3()), Cvoid, ())
169-
ccall((:fftwf_forget_wisdom,libfftw3f()), Cvoid, ())
171+
ccall((:fftw_forget_wisdom,libfftw3), Cvoid, ())
172+
ccall((:fftwf_forget_wisdom,libfftw3f), Cvoid, ())
170173
end
171174

172175
# Threads
@@ -176,15 +179,15 @@ function _set_num_threads(num_threads::Integer)
176179
@static if fftw_provider == "mkl"
177180
_last_num_threads[] = num_threads
178181
end
179-
ccall((:fftw_plan_with_nthreads,libfftw3()), Cvoid, (Int32,), num_threads)
180-
ccall((:fftwf_plan_with_nthreads,libfftw3f()), Cvoid, (Int32,), num_threads)
182+
ccall((:fftw_plan_with_nthreads,libfftw3), Cvoid, (Int32,), num_threads)
183+
ccall((:fftwf_plan_with_nthreads,libfftw3f), Cvoid, (Int32,), num_threads)
181184
end
182185

183186
@exclusive set_num_threads(num_threads::Integer) = _set_num_threads(num_threads)
184187

185188
function get_num_threads()
186189
@static if fftw_provider == "fftw"
187-
ccall((:fftw_planner_nthreads,libfftw3()), Cint, ())
190+
ccall((:fftw_planner_nthreads,libfftw3), Cint, ())
188191
else
189192
_last_num_threads[]
190193
end
@@ -211,9 +214,9 @@ const NO_TIMELIMIT = -1.0 # from fftw3.h
211214

212215
# only call these when fftwlock is held:
213216
unsafe_set_timelimit(precision::fftwTypeDouble,seconds) =
214-
ccall((:fftw_set_timelimit,libfftw3()), Cvoid, (Float64,), seconds)
217+
ccall((:fftw_set_timelimit,libfftw3), Cvoid, (Float64,), seconds)
215218
unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
216-
ccall((:fftwf_set_timelimit,libfftw3f()), Cvoid, (Float64,), seconds)
219+
ccall((:fftwf_set_timelimit,libfftw3f), Cvoid, (Float64,), seconds)
217220
@exclusive set_timelimit(precision, seconds) = unsafe_set_timelimit(precision, seconds)
218221

219222
# Array alignment mod 16:
@@ -234,9 +237,9 @@ unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
234237
convert(Int32, convert(Int64, pointer(A)) % 16)
235238
else
236239
alignment_of(A::StridedArray{T}) where {T<:fftwDouble} =
237-
ccall((:fftw_alignment_of, libfftw3()), Int32, (Ptr{T},), A)
240+
ccall((:fftw_alignment_of, libfftw3), Int32, (Ptr{T},), A)
238241
alignment_of(A::StridedArray{T}) where {T<:fftwSingle} =
239-
ccall((:fftwf_alignment_of, libfftw3f()), Int32, (Ptr{T},), A)
242+
ccall((:fftwf_alignment_of, libfftw3f), Int32, (Ptr{T},), A)
240243
end
241244

242245
# FFTWPlan (low-level)
@@ -320,9 +323,9 @@ unsafe_convert(::Type{PlanPtr}, p::FFTWPlan) = p.plan
320323

321324
# these functions should only be called while the fftwlock is held
322325
unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwDouble})) =
323-
ccall((:fftw_destroy_plan,libfftw3()), Cvoid, (PlanPtr,), plan)
326+
ccall((:fftw_destroy_plan,libfftw3), Cvoid, (PlanPtr,), plan)
324327
unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwSingle})) =
325-
ccall((:fftwf_destroy_plan,libfftw3f()), Cvoid, (PlanPtr,), plan)
328+
ccall((:fftwf_destroy_plan,libfftw3f), Cvoid, (PlanPtr,), plan)
326329

327330
const deferred_destroy_lock = ReentrantLock() # lock protecting the deferred_destroy_plans list
328331
const deferred_destroy_plans = FFTWPlan[]
@@ -383,19 +386,19 @@ end
383386
#################################################################################################
384387

385388
cost(plan::FFTWPlan{<:fftwDouble}) =
386-
ccall((:fftw_cost,libfftw3()), Float64, (PlanPtr,), plan)
389+
ccall((:fftw_cost,libfftw3), Float64, (PlanPtr,), plan)
387390
cost(plan::FFTWPlan{<:fftwSingle}) =
388-
ccall((:fftwf_cost,libfftw3f()), Float64, (PlanPtr,), plan)
391+
ccall((:fftwf_cost,libfftw3f), Float64, (PlanPtr,), plan)
389392

390393
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwDouble})
391394
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
392-
ccall((:fftw_flops,libfftw3()), Cvoid,
395+
ccall((:fftw_flops,libfftw3), Cvoid,
393396
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
394397
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
395398
end
396399
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwSingle})
397400
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
398-
ccall((:fftwf_flops,libfftw3f()), Cvoid,
401+
ccall((:fftwf_flops,libfftw3f), Cvoid,
399402
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
400403
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
401404
end
@@ -426,9 +429,9 @@ const has_sprint_plan = version >= v"3.3.4" && fftw_provider == "fftw"
426429

427430
@static if has_sprint_plan
428431
sprint_plan_(plan::FFTWPlan{<:fftwDouble}) =
429-
ccall((:fftw_sprint_plan,libfftw3()), Ptr{UInt8}, (PlanPtr,), plan)
432+
ccall((:fftw_sprint_plan,libfftw3), Ptr{UInt8}, (PlanPtr,), plan)
430433
sprint_plan_(plan::FFTWPlan{<:fftwSingle}) =
431-
ccall((:fftwf_sprint_plan,libfftw3f()), Ptr{UInt8}, (PlanPtr,), plan)
434+
ccall((:fftwf_sprint_plan,libfftw3f), Ptr{UInt8}, (PlanPtr,), plan)
432435
function sprint_plan(plan::FFTWPlan)
433436
p = sprint_plan_(plan)
434437
str = unsafe_string(p)
@@ -510,49 +513,49 @@ _colmajorstrides(p) = ()
510513
# Execute
511514

512515
unsafe_execute!(plan::FFTWPlan{<:fftwDouble}) =
513-
ccall((:fftw_execute,libfftw3()), Cvoid, (PlanPtr,), plan)
516+
ccall((:fftw_execute,libfftw3), Cvoid, (PlanPtr,), plan)
514517

515518
unsafe_execute!(plan::FFTWPlan{<:fftwSingle}) =
516-
ccall((:fftwf_execute,libfftw3f()), Cvoid, (PlanPtr,), plan)
519+
ccall((:fftwf_execute,libfftw3f), Cvoid, (PlanPtr,), plan)
517520

518521
unsafe_execute!(plan::cFFTWPlan{T},
519522
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
520-
ccall((:fftw_execute_dft,libfftw3()), Cvoid,
523+
ccall((:fftw_execute_dft,libfftw3), Cvoid,
521524
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
522525

523526
unsafe_execute!(plan::cFFTWPlan{T},
524527
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
525-
ccall((:fftwf_execute_dft,libfftw3f()), Cvoid,
528+
ccall((:fftwf_execute_dft,libfftw3f), Cvoid,
526529
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
527530

528531
unsafe_execute!(plan::rFFTWPlan{Float64,FORWARD},
529532
X::StridedArray{Float64}, Y::StridedArray{Complex{Float64}}) =
530-
ccall((:fftw_execute_dft_r2c,libfftw3()), Cvoid,
533+
ccall((:fftw_execute_dft_r2c,libfftw3), Cvoid,
531534
(PlanPtr,Ptr{Float64},Ptr{Complex{Float64}}), plan, X, Y)
532535

533536
unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD},
534537
X::StridedArray{Float32}, Y::StridedArray{Complex{Float32}}) =
535-
ccall((:fftwf_execute_dft_r2c,libfftw3f()), Cvoid,
538+
ccall((:fftwf_execute_dft_r2c,libfftw3f), Cvoid,
536539
(PlanPtr,Ptr{Float32},Ptr{Complex{Float32}}), plan, X, Y)
537540

538541
unsafe_execute!(plan::rFFTWPlan{Complex{Float64},BACKWARD},
539542
X::StridedArray{Complex{Float64}}, Y::StridedArray{Float64}) =
540-
ccall((:fftw_execute_dft_c2r,libfftw3()), Cvoid,
543+
ccall((:fftw_execute_dft_c2r,libfftw3), Cvoid,
541544
(PlanPtr,Ptr{Complex{Float64}},Ptr{Float64}), plan, X, Y)
542545

543546
unsafe_execute!(plan::rFFTWPlan{Complex{Float32},BACKWARD},
544547
X::StridedArray{Complex{Float32}}, Y::StridedArray{Float32}) =
545-
ccall((:fftwf_execute_dft_c2r,libfftw3f()), Cvoid,
548+
ccall((:fftwf_execute_dft_c2r,libfftw3f), Cvoid,
546549
(PlanPtr,Ptr{Complex{Float32}},Ptr{Float32}), plan, X, Y)
547550

548551
unsafe_execute!(plan::r2rFFTWPlan{T},
549552
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
550-
ccall((:fftw_execute_r2r,libfftw3()), Cvoid,
553+
ccall((:fftw_execute_r2r,libfftw3), Cvoid,
551554
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
552555

553556
unsafe_execute!(plan::r2rFFTWPlan{T},
554557
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
555-
ccall((:fftwf_execute_r2r,libfftw3f()), Cvoid,
558+
ccall((:fftwf_execute_r2r,libfftw3f), Cvoid,
556559
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
557560

558561
# NOTE ON GC (garbage collection):
@@ -649,7 +652,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
649652
unsafe_set_timelimit($Tr, timelimit)
650653
R = isa(region, Tuple) ? region : copy(region)
651654
dims, howmany = dims_howmany(X, Y, size(X), R)
652-
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib()),
655+
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib),
653656
PlanPtr,
654657
(Int32, Ptr{Int}, Int32, Ptr{Int},
655658
Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32),
@@ -669,7 +672,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
669672
regionshft = _circshiftmin1(region) # FFTW halves last dim
670673
unsafe_set_timelimit($Tr, timelimit)
671674
dims, howmany = dims_howmany(X, Y, size(X), regionshft)
672-
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib()),
675+
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib),
673676
PlanPtr,
674677
(Int32, Ptr{Int}, Int32, Ptr{Int},
675678
Ptr{$Tr}, Ptr{$Tc}, UInt32),
@@ -689,7 +692,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
689692
regionshft = _circshiftmin1(region) # FFTW halves last dim
690693
unsafe_set_timelimit($Tr, timelimit)
691694
dims, howmany = dims_howmany(X, Y, size(Y), regionshft)
692-
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib()),
695+
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib),
693696
PlanPtr,
694697
(Int32, Ptr{Int}, Int32, Ptr{Int},
695698
Ptr{$Tc}, Ptr{$Tr}, UInt32),
@@ -711,7 +714,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
711714
knd = fix_kinds(region, kinds)
712715
unsafe_set_timelimit($Tr, timelimit)
713716
dims, howmany = dims_howmany(X, Y, size(X), region)
714-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
717+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
715718
PlanPtr,
716719
(Int32, Ptr{Int}, Int32, Ptr{Int},
717720
Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32),
@@ -739,7 +742,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
739742
howmany[2:3, :] .*= 2
740743
end
741744
howmany = [howmany [2,1,1]] # append loop over real/imag parts
742-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
745+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
743746
PlanPtr,
744747
(Int32, Ptr{Int}, Int32, Ptr{Int},
745748
Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32),

0 commit comments

Comments
 (0)