Skip to content

Commit eda5550

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 dbcb5d2 commit eda5550

File tree

5 files changed

+103
-90
lines changed

5 files changed

+103
-90
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
name = "FFTW"
22
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
3-
version = "1.9.0"
3+
version = "1.10.0"
44

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"
@@ -13,6 +14,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1314
[compat]
1415
AbstractFFTs = "1.5"
1516
FFTW_jll = "3.3.9"
17+
Libdl = "1.6"
1618
LinearAlgebra = "<0.0.1, 1"
1719
MKL_jll = "2019.0.117, 2020, 2021, 2022, 2023, 2024, 2025"
1820
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[]
@@ -388,19 +391,19 @@ end
388391
#################################################################################################
389392

390393
cost(plan::FFTWPlan{<:fftwDouble}) =
391-
ccall((:fftw_cost,libfftw3()), Float64, (PlanPtr,), plan)
394+
ccall((:fftw_cost,libfftw3), Float64, (PlanPtr,), plan)
392395
cost(plan::FFTWPlan{<:fftwSingle}) =
393-
ccall((:fftwf_cost,libfftw3f()), Float64, (PlanPtr,), plan)
396+
ccall((:fftwf_cost,libfftw3f), Float64, (PlanPtr,), plan)
394397

395398
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwDouble})
396399
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
397-
ccall((:fftw_flops,libfftw3()), Cvoid,
400+
ccall((:fftw_flops,libfftw3), Cvoid,
398401
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
399402
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
400403
end
401404
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwSingle})
402405
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
403-
ccall((:fftwf_flops,libfftw3f()), Cvoid,
406+
ccall((:fftwf_flops,libfftw3f), Cvoid,
404407
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
405408
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
406409
end
@@ -431,9 +434,9 @@ const has_sprint_plan = version >= v"3.3.4" && fftw_provider == "fftw"
431434

432435
@static if has_sprint_plan
433436
sprint_plan_(plan::FFTWPlan{<:fftwDouble}) =
434-
ccall((:fftw_sprint_plan,libfftw3()), Ptr{UInt8}, (PlanPtr,), plan)
437+
ccall((:fftw_sprint_plan,libfftw3), Ptr{UInt8}, (PlanPtr,), plan)
435438
sprint_plan_(plan::FFTWPlan{<:fftwSingle}) =
436-
ccall((:fftwf_sprint_plan,libfftw3f()), Ptr{UInt8}, (PlanPtr,), plan)
439+
ccall((:fftwf_sprint_plan,libfftw3f), Ptr{UInt8}, (PlanPtr,), plan)
437440
function sprint_plan(plan::FFTWPlan)
438441
p = sprint_plan_(plan)
439442
str = unsafe_string(p)
@@ -515,49 +518,49 @@ _colmajorstrides(p) = ()
515518
# Execute
516519

517520
unsafe_execute!(plan::FFTWPlan{<:fftwDouble}) =
518-
ccall((:fftw_execute,libfftw3()), Cvoid, (PlanPtr,), plan)
521+
ccall((:fftw_execute,libfftw3), Cvoid, (PlanPtr,), plan)
519522

520523
unsafe_execute!(plan::FFTWPlan{<:fftwSingle}) =
521-
ccall((:fftwf_execute,libfftw3f()), Cvoid, (PlanPtr,), plan)
524+
ccall((:fftwf_execute,libfftw3f), Cvoid, (PlanPtr,), plan)
522525

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

528531
unsafe_execute!(plan::cFFTWPlan{T},
529532
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
530-
ccall((:fftwf_execute_dft,libfftw3f()), Cvoid,
533+
ccall((:fftwf_execute_dft,libfftw3f), Cvoid,
531534
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
532535

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

538541
unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD},
539542
X::StridedArray{Float32}, Y::StridedArray{Complex{Float32}}) =
540-
ccall((:fftwf_execute_dft_r2c,libfftw3f()), Cvoid,
543+
ccall((:fftwf_execute_dft_r2c,libfftw3f), Cvoid,
541544
(PlanPtr,Ptr{Float32},Ptr{Complex{Float32}}), plan, X, Y)
542545

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

548551
unsafe_execute!(plan::rFFTWPlan{Complex{Float32},BACKWARD},
549552
X::StridedArray{Complex{Float32}}, Y::StridedArray{Float32}) =
550-
ccall((:fftwf_execute_dft_c2r,libfftw3f()), Cvoid,
553+
ccall((:fftwf_execute_dft_c2r,libfftw3f), Cvoid,
551554
(PlanPtr,Ptr{Complex{Float32}},Ptr{Float32}), plan, X, Y)
552555

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

558561
unsafe_execute!(plan::r2rFFTWPlan{T},
559562
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
560-
ccall((:fftwf_execute_r2r,libfftw3f()), Cvoid,
563+
ccall((:fftwf_execute_r2r,libfftw3f), Cvoid,
561564
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
562565

563566
# NOTE ON GC (garbage collection):
@@ -654,7 +657,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
654657
unsafe_set_timelimit($Tr, timelimit)
655658
R = isa(region, Tuple) ? region : copy(region)
656659
dims, howmany = dims_howmany(X, Y, size(X), R)
657-
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib()),
660+
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib),
658661
PlanPtr,
659662
(Int32, Ptr{Int}, Int32, Ptr{Int},
660663
Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32),
@@ -674,7 +677,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
674677
regionshft = _circshiftmin1(region) # FFTW halves last dim
675678
unsafe_set_timelimit($Tr, timelimit)
676679
dims, howmany = dims_howmany(X, Y, size(X), regionshft)
677-
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib()),
680+
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib),
678681
PlanPtr,
679682
(Int32, Ptr{Int}, Int32, Ptr{Int},
680683
Ptr{$Tr}, Ptr{$Tc}, UInt32),
@@ -694,7 +697,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
694697
regionshft = _circshiftmin1(region) # FFTW halves last dim
695698
unsafe_set_timelimit($Tr, timelimit)
696699
dims, howmany = dims_howmany(X, Y, size(Y), regionshft)
697-
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib()),
700+
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib),
698701
PlanPtr,
699702
(Int32, Ptr{Int}, Int32, Ptr{Int},
700703
Ptr{$Tc}, Ptr{$Tr}, UInt32),
@@ -716,7 +719,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
716719
knd = fix_kinds(region, kinds)
717720
unsafe_set_timelimit($Tr, timelimit)
718721
dims, howmany = dims_howmany(X, Y, size(X), region)
719-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
722+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
720723
PlanPtr,
721724
(Int32, Ptr{Int}, Int32, Ptr{Int},
722725
Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32),
@@ -744,7 +747,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
744747
howmany[2:3, :] .*= 2
745748
end
746749
howmany = [howmany [2,1,1]] # append loop over real/imag parts
747-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
750+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
748751
PlanPtr,
749752
(Int32, Ptr{Int}, Int32, Ptr{Int},
750753
Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32),

0 commit comments

Comments
 (0)