Skip to content

Commit 4401d48

Browse files
authored
Revert back to the standard form for ccall which now supports LazyLibrary improvements (#318)
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. Refs: [ccall: make distinction of pointer vs name a syntactic distinction](JuliaLang/julia#59165)
1 parent 92ee1af commit 4401d48

File tree

5 files changed

+97
-87
lines changed

5 files changed

+97
-87
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: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,67 @@ export dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct!
1616

1717
include("providers.jl")
1818

19-
function initialize_library_paths()
19+
function fftw_init_check()
2020
# If someone is trying to set the provider via the old environment variable, warn them that they
2121
# should instead use `set_provider!()` instead.
2222
if haskey(ENV, "JULIA_FFTW_PROVIDER")
2323
Base.depwarn("JULIA_FFTW_PROVIDER is deprecated; use FFTW.set_provider!() instead", :JULIA_FFTW_PROVIDER)
2424
end
2525

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.
26+
# Hook FFTW threads up to our partr runtime
2927
@static if fftw_provider == "fftw"
30-
libfftw3_path[] = FFTW_jll.libfftw3_path
31-
libfftw3f_path[] = FFTW_jll.libfftw3f_path
3228
fftw_init_threads()
3329
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
3930
end
4031

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[]
32+
if VERSION >= v"1.11.0"
33+
# This can be be deleted once FFTW_jll is upgraded to the real lazy jll code, to get the real benefits of this mess
34+
mutable struct FakeLazyLibrary
35+
reallibrary::Symbol
36+
on_load_callback
37+
@atomic h::Ptr{Cvoid}
38+
end
39+
import Libdl: LazyLibrary, dlopen
40+
function dlopen(lib::FakeLazyLibrary)
41+
h = @atomic :monotonic lib.h
42+
h != C_NULL && return h
43+
@lock fftwlock begin
44+
h = @atomic :monotonic lib.h
45+
h != C_NULL && return h
46+
h = dlopen(getglobal(FFTW, lib.reallibrary))
47+
lib.on_load_callback()
48+
@atomic :release lib.h = h
5349
end
50+
return h
51+
end
52+
53+
@static if fftw_provider == "fftw"
54+
import FFTW_jll: libfftw3 as libfftw3_no_init,
55+
libfftw3f as libfftw3f_no_init
56+
elseif fftw_provider == "mkl"
57+
import MKL_jll: libmkl_rt as libfftw3_no_init,
58+
libmkl_rt as libfftw3f_no_init
59+
end
60+
const libfftw3 = FakeLazyLibrary(:libfftw3_no_init, fftw_init_check, C_NULL)
61+
const libfftw3f = FakeLazyLibrary(:libfftw3f_no_init, fftw_init_check, C_NULL)
62+
5463
else
55-
function __init__()
56-
initialize_library_paths()
57-
end
58-
libfftw3() = libfftw3_path[]
59-
libfftw3f() = libfftw3f_path[]
64+
@static if fftw_provider == "fftw"
65+
import FFTW_jll: libfftw3_path as libfftw3_no_init,
66+
libfftw3f_path as libfftw3f_no_init,
67+
libfftw3_path as libfftw3,
68+
libfftw3f_path as libfftw3f
69+
elseif fftw_provider == "mkl"
70+
import MKL_jll: libmkl_rt_path as libfftw3_no_init,
71+
libmkl_rt_path as libfftw3f_no_init,
72+
libmkl_rt_path as libfftw3,
73+
libmkl_rt_path as libfftw3f
6074
end
75+
function __init__()
76+
fftw_init_check()
77+
end
78+
end
79+
6180

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

src/fft.jl

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

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

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

6063
## Direction of FFT
6164

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

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

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

166169
@exclusive function forget_wisdom()
167-
ccall((:fftw_forget_wisdom,libfftw3()), Cvoid, ())
168-
ccall((:fftwf_forget_wisdom,libfftw3f()), Cvoid, ())
170+
ccall((:fftw_forget_wisdom,libfftw3), Cvoid, ())
171+
ccall((:fftwf_forget_wisdom,libfftw3f), Cvoid, ())
169172
end
170173

171174
# Threads
@@ -175,15 +178,15 @@ function _set_num_threads(num_threads::Integer)
175178
@static if fftw_provider == "mkl"
176179
_last_num_threads[] = num_threads
177180
end
178-
ccall((:fftw_plan_with_nthreads,libfftw3()), Cvoid, (Int32,), num_threads)
179-
ccall((:fftwf_plan_with_nthreads,libfftw3f()), Cvoid, (Int32,), num_threads)
181+
ccall((:fftw_plan_with_nthreads,libfftw3), Cvoid, (Int32,), num_threads)
182+
ccall((:fftwf_plan_with_nthreads,libfftw3f), Cvoid, (Int32,), num_threads)
180183
end
181184

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

184187
function get_num_threads()
185188
@static if fftw_provider == "fftw"
186-
ccall((:fftw_planner_nthreads,libfftw3()), Cint, ())
189+
ccall((:fftw_planner_nthreads,libfftw3), Cint, ())
187190
else
188191
_last_num_threads[]
189192
end
@@ -210,9 +213,9 @@ const NO_TIMELIMIT = -1.0 # from fftw3.h
210213

211214
# only call these when fftwlock is held:
212215
unsafe_set_timelimit(precision::fftwTypeDouble,seconds) =
213-
ccall((:fftw_set_timelimit,libfftw3()), Cvoid, (Float64,), seconds)
216+
ccall((:fftw_set_timelimit,libfftw3), Cvoid, (Float64,), seconds)
214217
unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
215-
ccall((:fftwf_set_timelimit,libfftw3f()), Cvoid, (Float64,), seconds)
218+
ccall((:fftwf_set_timelimit,libfftw3f), Cvoid, (Float64,), seconds)
216219
@exclusive set_timelimit(precision, seconds) = unsafe_set_timelimit(precision, seconds)
217220

218221
# Array alignment mod 16:
@@ -233,9 +236,9 @@ unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
233236
convert(Int32, convert(Int64, pointer(A)) % 16)
234237
else
235238
alignment_of(A::StridedArray{T}) where {T<:fftwDouble} =
236-
ccall((:fftw_alignment_of, libfftw3()), Int32, (Ptr{T},), A)
239+
ccall((:fftw_alignment_of, libfftw3), Int32, (Ptr{T},), A)
237240
alignment_of(A::StridedArray{T}) where {T<:fftwSingle} =
238-
ccall((:fftwf_alignment_of, libfftw3f()), Int32, (Ptr{T},), A)
241+
ccall((:fftwf_alignment_of, libfftw3f), Int32, (Ptr{T},), A)
239242
end
240243

241244
# FFTWPlan (low-level)
@@ -319,9 +322,9 @@ unsafe_convert(::Type{PlanPtr}, p::FFTWPlan) = p.plan
319322

320323
# these functions should only be called while the fftwlock is held
321324
unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwDouble})) =
322-
ccall((:fftw_destroy_plan,libfftw3()), Cvoid, (PlanPtr,), plan)
325+
ccall((:fftw_destroy_plan,libfftw3), Cvoid, (PlanPtr,), plan)
323326
unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwSingle})) =
324-
ccall((:fftwf_destroy_plan,libfftw3f()), Cvoid, (PlanPtr,), plan)
327+
ccall((:fftwf_destroy_plan,libfftw3f), Cvoid, (PlanPtr,), plan)
325328

326329
const deferred_destroy_lock = ReentrantLock() # lock protecting the deferred_destroy_plans list
327330
const deferred_destroy_plans = FFTWPlan[]
@@ -382,19 +385,19 @@ end
382385
#################################################################################################
383386

384387
cost(plan::FFTWPlan{<:fftwDouble}) =
385-
ccall((:fftw_cost,libfftw3()), Float64, (PlanPtr,), plan)
388+
ccall((:fftw_cost,libfftw3), Float64, (PlanPtr,), plan)
386389
cost(plan::FFTWPlan{<:fftwSingle}) =
387-
ccall((:fftwf_cost,libfftw3f()), Float64, (PlanPtr,), plan)
390+
ccall((:fftwf_cost,libfftw3f), Float64, (PlanPtr,), plan)
388391

389392
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwDouble})
390393
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
391-
ccall((:fftw_flops,libfftw3()), Cvoid,
394+
ccall((:fftw_flops,libfftw3), Cvoid,
392395
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
393396
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
394397
end
395398
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwSingle})
396399
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
397-
ccall((:fftwf_flops,libfftw3f()), Cvoid,
400+
ccall((:fftwf_flops,libfftw3f), 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
@@ -425,9 +428,9 @@ const has_sprint_plan = version >= v"3.3.4" && fftw_provider == "fftw"
425428

426429
@static if has_sprint_plan
427430
sprint_plan_(plan::FFTWPlan{<:fftwDouble}) =
428-
ccall((:fftw_sprint_plan,libfftw3()), Ptr{UInt8}, (PlanPtr,), plan)
431+
ccall((:fftw_sprint_plan,libfftw3), Ptr{UInt8}, (PlanPtr,), plan)
429432
sprint_plan_(plan::FFTWPlan{<:fftwSingle}) =
430-
ccall((:fftwf_sprint_plan,libfftw3f()), Ptr{UInt8}, (PlanPtr,), plan)
433+
ccall((:fftwf_sprint_plan,libfftw3f), Ptr{UInt8}, (PlanPtr,), plan)
431434
function sprint_plan(plan::FFTWPlan)
432435
p = sprint_plan_(plan)
433436
str = unsafe_string(p)
@@ -509,49 +512,49 @@ _colmajorstrides(p) = ()
509512
# Execute
510513

511514
unsafe_execute!(plan::FFTWPlan{<:fftwDouble}) =
512-
ccall((:fftw_execute,libfftw3()), Cvoid, (PlanPtr,), plan)
515+
ccall((:fftw_execute,libfftw3), Cvoid, (PlanPtr,), plan)
513516

514517
unsafe_execute!(plan::FFTWPlan{<:fftwSingle}) =
515-
ccall((:fftwf_execute,libfftw3f()), Cvoid, (PlanPtr,), plan)
518+
ccall((:fftwf_execute,libfftw3f), Cvoid, (PlanPtr,), plan)
516519

517520
unsafe_execute!(plan::cFFTWPlan{T},
518521
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
519-
ccall((:fftw_execute_dft,libfftw3()), Cvoid,
522+
ccall((:fftw_execute_dft,libfftw3), Cvoid,
520523
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
521524

522525
unsafe_execute!(plan::cFFTWPlan{T},
523526
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
524-
ccall((:fftwf_execute_dft,libfftw3f()), Cvoid,
527+
ccall((:fftwf_execute_dft,libfftw3f), Cvoid,
525528
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
526529

527530
unsafe_execute!(plan::rFFTWPlan{Float64,FORWARD},
528531
X::StridedArray{Float64}, Y::StridedArray{Complex{Float64}}) =
529-
ccall((:fftw_execute_dft_r2c,libfftw3()), Cvoid,
532+
ccall((:fftw_execute_dft_r2c,libfftw3), Cvoid,
530533
(PlanPtr,Ptr{Float64},Ptr{Complex{Float64}}), plan, X, Y)
531534

532535
unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD},
533536
X::StridedArray{Float32}, Y::StridedArray{Complex{Float32}}) =
534-
ccall((:fftwf_execute_dft_r2c,libfftw3f()), Cvoid,
537+
ccall((:fftwf_execute_dft_r2c,libfftw3f), Cvoid,
535538
(PlanPtr,Ptr{Float32},Ptr{Complex{Float32}}), plan, X, Y)
536539

537540
unsafe_execute!(plan::rFFTWPlan{Complex{Float64},BACKWARD},
538541
X::StridedArray{Complex{Float64}}, Y::StridedArray{Float64}) =
539-
ccall((:fftw_execute_dft_c2r,libfftw3()), Cvoid,
542+
ccall((:fftw_execute_dft_c2r,libfftw3), Cvoid,
540543
(PlanPtr,Ptr{Complex{Float64}},Ptr{Float64}), plan, X, Y)
541544

542545
unsafe_execute!(plan::rFFTWPlan{Complex{Float32},BACKWARD},
543546
X::StridedArray{Complex{Float32}}, Y::StridedArray{Float32}) =
544-
ccall((:fftwf_execute_dft_c2r,libfftw3f()), Cvoid,
547+
ccall((:fftwf_execute_dft_c2r,libfftw3f), Cvoid,
545548
(PlanPtr,Ptr{Complex{Float32}},Ptr{Float32}), plan, X, Y)
546549

547550
unsafe_execute!(plan::r2rFFTWPlan{T},
548551
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
549-
ccall((:fftw_execute_r2r,libfftw3()), Cvoid,
552+
ccall((:fftw_execute_r2r,libfftw3), Cvoid,
550553
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
551554

552555
unsafe_execute!(plan::r2rFFTWPlan{T},
553556
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
554-
ccall((:fftwf_execute_r2r,libfftw3f()), Cvoid,
557+
ccall((:fftwf_execute_r2r,libfftw3f), Cvoid,
555558
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)
556559

557560
# NOTE ON GC (garbage collection):
@@ -648,7 +651,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
648651
unsafe_set_timelimit($Tr, timelimit)
649652
R = isa(region, Tuple) ? region : copy(region)
650653
dims, howmany = dims_howmany(X, Y, size(X), R)
651-
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib()),
654+
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib),
652655
PlanPtr,
653656
(Int32, Ptr{Int}, Int32, Ptr{Int},
654657
Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32),
@@ -668,7 +671,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
668671
regionshft = _circshiftmin1(region) # FFTW halves last dim
669672
unsafe_set_timelimit($Tr, timelimit)
670673
dims, howmany = dims_howmany(X, Y, size(X), regionshft)
671-
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib()),
674+
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib),
672675
PlanPtr,
673676
(Int32, Ptr{Int}, Int32, Ptr{Int},
674677
Ptr{$Tr}, Ptr{$Tc}, UInt32),
@@ -688,7 +691,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
688691
regionshft = _circshiftmin1(region) # FFTW halves last dim
689692
unsafe_set_timelimit($Tr, timelimit)
690693
dims, howmany = dims_howmany(X, Y, size(Y), regionshft)
691-
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib()),
694+
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib),
692695
PlanPtr,
693696
(Int32, Ptr{Int}, Int32, Ptr{Int},
694697
Ptr{$Tc}, Ptr{$Tr}, UInt32),
@@ -710,7 +713,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
710713
knd = fix_kinds(region, kinds)
711714
unsafe_set_timelimit($Tr, timelimit)
712715
dims, howmany = dims_howmany(X, Y, size(X), region)
713-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
716+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
714717
PlanPtr,
715718
(Int32, Ptr{Int}, Int32, Ptr{Int},
716719
Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32),
@@ -738,7 +741,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
738741
howmany[2:3, :] .*= 2
739742
end
740743
howmany = [howmany [2,1,1]] # append loop over real/imag parts
741-
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
744+
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
742745
PlanPtr,
743746
(Int32, Ptr{Int}, Int32, Ptr{Int},
744747
Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32),

0 commit comments

Comments
 (0)