@@ -9,7 +9,7 @@ import Base.DL_LOAD_PATH
99
1010export  DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL,
1111    RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e,
12-     dlpath, find_library, dlext, dllist
12+     dlpath, find_library, dlext, dllist, LazyLibrary 
1313
1414""" 
1515    DL_LOAD_PATH 
@@ -45,6 +45,9 @@ applicable.
4545""" 
4646(RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW)
4747
48+ #  The default flags for `dlopen()`
49+ const  default_rtld_flags =  RTLD_LAZY |  RTLD_DEEPBIND
50+ 
4851""" 
4952    dlsym(handle, sym; throw_error::Bool = true) 
5053
7275Look up a symbol from a shared library handle, silently return `C_NULL` on lookup failure. 
7376This method is now deprecated in favor of `dlsym(handle, sym; throw_error=false)`. 
7477""" 
75- function  dlsym_e (hnd :: Ptr , s :: Union{Symbol,AbstractString} )
76-     return  something (dlsym (hnd, s ; throw_error= false ), C_NULL )
78+ function  dlsym_e (args ... )
79+     return  something (dlsym (args ... ; throw_error= false ), C_NULL )
7780end 
7881
7982""" 
@@ -110,10 +113,10 @@ If the library cannot be found, this method throws an error, unless the keyword
110113""" 
111114function  dlopen end 
112115
113- dlopen (s:: Symbol , flags:: Integer  =  RTLD_LAZY  |  RTLD_DEEPBIND ; kwargs... ) = 
116+ dlopen (s:: Symbol , flags:: Integer  =  default_rtld_flags ; kwargs... ) = 
114117    dlopen (string (s), flags; kwargs... )
115118
116- function  dlopen (s:: AbstractString , flags:: Integer  =  RTLD_LAZY  |  RTLD_DEEPBIND ; throw_error:: Bool  =  true )
119+ function  dlopen (s:: AbstractString , flags:: Integer  =  default_rtld_flags ; throw_error:: Bool  =  true )
117120    ret =  ccall (:jl_load_dynamic_library , Ptr{Cvoid}, (Cstring,UInt32,Cint), s, flags, Cint (throw_error))
118121    if  ret ==  C_NULL 
119122        return  nothing 
@@ -226,23 +229,6 @@ function dlpath(handle::Ptr{Cvoid})
226229    return  s
227230end 
228231
229- """ 
230-     dlpath(libname::Union{AbstractString, Symbol}) 
231- 
232- Get the full path of the library `libname`. 
233- 
234- # Example 
235- ```julia-repl 
236- julia> dlpath("libjulia") 
237- ``` 
238- """ 
239- function  dlpath (libname:: Union{AbstractString, Symbol} )
240-     handle =  dlopen (libname)
241-     path =  dlpath (handle)
242-     dlclose (handle)
243-     return  path
244- end 
245- 
246232if  Sys. isapple ()
247233    const  dlext =  " dylib" 
248234elseif  Sys. iswindows ()
@@ -314,4 +300,101 @@ function dllist()
314300    return  dynamic_libraries
315301end 
316302
303+ 
304+ """ 
305+     LazyLibrary(name, flags = <default dlopen flags>, 
306+                 dependencies = LazyLibrary[], on_load_callback = nothing) 
307+ 
308+ Represents a lazily-loaded library that opens itself and its dependencies on first usage 
309+ in a `dlopen()`, `dlsym()`, or `ccall()` usage.  While this structure contains the 
310+ ability to run arbitrary code on first load via `on_load_callback`, we caution that this 
311+ should be used sparingly, as it is not expected that `ccall()` should result in large 
312+ amounts of Julia code being run. 
313+ """ 
314+ mutable struct  LazyLibrary
315+     #  Name and flags to open with
316+     const  name:: String 
317+     const  flags:: UInt32 
318+ 
319+     #  Dependencies that must be loaded before we can load
320+     const  dependencies:: Vector{LazyLibrary} 
321+ 
322+     #  Function that gets called once upon initial load with the pointer as an argument
323+     const  on_load_callback:: Union{Nothing,Function} 
324+     #  Function that gets called once upon final unload with the pointer as an argument
325+     const  on_unload_callback:: Union{Nothing,Function} 
326+ 
327+     #  Pointer that we eventually fill out upon first `dlopen()`
328+     @atomic  handle:: Ptr{Cvoid} 
329+     @atomic  refs:: Int 
330+     function  LazyLibrary (name, flags =  default_rtld_flags, dependencies =  LazyLibrary[],
331+                          on_load_callback =  nothing , on_unload_callback =  nothing )
332+         return  new (
333+             String (name),
334+             UInt32 (flags),
335+             collect (dependencies),
336+             on_load_callback,
337+             on_unload_callback,
338+             C_NULL ,
339+             0 ,
340+         )
341+     end 
342+ end 
343+ 
344+ function  dlopen (ll:: LazyLibrary , flags:: Integer  =  ll. flags; kwargs... )
345+     #  Only load once
346+     handle =  @atomic  ll. handle
347+     if  handle !=  C_NULL 
348+         @atomic  ll. refs +=  1 
349+         return  handle
350+     end 
351+ 
352+     #  Ensure that all dependencies are loaded
353+     for  dep in  ll. dependencies
354+         dlopen (dep; kwargs... )
355+     end 
356+ 
357+     #  Load our library
358+     handle =  dlopen (ll. name, flags; kwargs... )
359+     @atomic  ll. handle =  handle
360+     @atomic  ll. refs +=  1 
361+ 
362+     #  Invoke our on load callback, if it exists
363+     if  ll. on_load_callback != =  nothing 
364+         ll. on_load_callback (handle)
365+     end 
366+     return  handle
367+ end 
368+ dlsym (ll:: LazyLibrary , args... ; kwargs... ) =  dlsym (dlopen (ll), args... ; kwargs... )
369+ function  dlclose (ll:: LazyLibrary )
370+     if  @atomic (ll. refs) >  0  &&  @atomic (ll. handle) !=  C_NULL 
371+         @atomic  ll. refs -=  1 
372+         if  @atomic (ll. refs) <=  0 
373+             if  ll. on_unload_callback != =  nothing 
374+                 ll. on_unload_callback (@atomic (ll. handle))
375+             end 
376+             dlclose (@atomic (ll. handle))
377+             @atomic  ll. handle =  C_NULL 
378+         end 
379+     end 
380+ end 
381+ 
382+ """ 
383+     dlpath(libname) 
384+ 
385+ Get the full path of the library `libname`.  `libname` can be a string, a symbol 
386+ or a `LazyLibrary` object. 
387+ 
388+ # Example 
389+ ```julia-repl 
390+ julia> dlpath("libjulia") 
391+ ``` 
392+ """ 
393+ function  dlpath (libname:: Union{AbstractString, Symbol, LazyLibrary} )
394+     handle =  dlopen (libname)
395+     path =  dlpath (handle)
396+     dlclose (handle)
397+     return  path
398+ end 
399+ 
317400end  #  module Libdl
0 commit comments