@@ -854,18 +854,135 @@ it must be handled in other ways.
854854
855855In some cases, the exact name or path of the needed library is not known in
856856advance and must be computed at run time. To handle such cases, the library
857- component specification can be a value such as ` Libdl.LazyLibrary ` . For
858- example, in ` @ccall blas.dgemm() ` , there can be a global defined as `const blas
859- = LazyLibrary("libblas")` . The runtime will call  ` dlsym(: dgemm , dlopen(blas))`
860- when the ` @ccall `  itself is executed. The ` Libdl.dlopen `  function can be
861- overloaded for custom types to provide alternate behaviors. However, it is
862- assumed that the library location does not change once it is determined, so the
863- result of the call can be cached and reused. Therefore, the number of times the
864- expression executes is unspecified, and returning different values for multiple
865- calls results in unspecified behavior.
866- 
867- If even more flexibility is needed, it is possible
868- to use computed values as function names by staging through [ ` eval ` ] ( @ref )  as follows:
857+ component specification can be a value such as ` Libdl.LazyLibrary ` . The runtime
858+ will call ` Libdl.dlopen `  on that object when first used by a ` ccall ` .
859+ 
860+ ### [ Using LazyLibrary for Lazy Loading] (@id   man-lazylibrary) 
861+ 
862+ [ ` Libdl.LazyLibrary ` ] ( @ref )  provides a thread-safe mechanism for deferring library loading
863+ until first use. This is the recommended approach for library initialization in modern Julia code.
864+ 
865+ A ` LazyLibrary `  represents a library that opens itself (and its dependencies) automatically
866+ on first use in a ` ccall() ` , ` @ccall ` , ` dlopen() ` , ` dlsym() ` , ` dlpath() ` , or ` cglobal() ` .
867+ The library is loaded exactly once in a thread-safe manner, and subsequent calls reuse the
868+ loaded library handle.
869+ 
870+ #### Basic Usage  
871+ 
872+ ``` julia 
873+ using  Libdl
874+ 
875+ #  Define a LazyLibrary as a const for optimal performance
876+ const  libz =  LazyLibrary (" libz" 
877+ 
878+ #  Use directly in @ccall - library loads automatically on first call
879+ @ccall  libz. deflate (strm:: Ptr{Cvoid} , flush:: Cint ):: Cint 
880+ 
881+ #  Also works with ccall
882+ ccall ((:inflate , libz), Cint, (Ptr{Cvoid}, Cint), strm, flush)
883+ ``` 
884+ 
885+ #### Platform-Specific Libraries  
886+ 
887+ For code that needs to work across different platforms:
888+ 
889+ ``` julia 
890+ const  mylib =  LazyLibrary (
891+     if  Sys. iswindows ()
892+         " mylib.dll" 
893+     elseif  Sys. isapple ()
894+         " libmylib.dylib" 
895+     else 
896+         " libmylib.so" 
897+     end 
898+ )
899+ ``` 
900+ 
901+ #### Libraries with Dependencies  
902+ 
903+ When a library depends on other libraries, specify the dependencies to ensure
904+ they load in the correct order:
905+ 
906+ ``` julia 
907+ const  libfoo =  LazyLibrary (" libfoo" 
908+ const  libbar =  LazyLibrary (" libbar" = [libfoo])
909+ 
910+ #  When libbar is first used, libfoo is loaded first automatically
911+ @ccall  libbar. bar_function (x:: Cint ):: Cint 
912+ ``` 
913+ 
914+ #### Lazy Path Construction  
915+ 
916+ For libraries whose paths are determined at runtime, use ` LazyLibraryPath ` :
917+ 
918+ ``` julia 
919+ #  Path is constructed when library is first accessed
920+ const  mylib =  LazyLibrary (LazyLibraryPath (artifact_dir, " lib" " libmylib.so" 
921+ ``` 
922+ 
923+ #### Initialization Callbacks  
924+ 
925+ If a library requires initialization after loading:
926+ 
927+ ``` julia 
928+ const  mylib =  LazyLibrary (" libmylib" 
929+     on_load_callback =  () ->  @ccall  mylib. initialize ():: Cvoid 
930+ )
931+ ``` 
932+ 
933+ !!! warning
934+     The ` on_load_callback `  should be minimal and must not call ` wait() `  on any tasks.
935+     It is called exactly once by the thread that loads the library.
936+ 
937+ #### Conversion from ` __init__() `  Pattern  
938+ 
939+ Before ` LazyLibrary ` , library paths were often computed in ` __init__() `  functions.
940+ This pattern can be replaced with ` LazyLibrary `  for better performance and thread safety.
941+ 
942+ Old pattern using ` __init__() ` :
943+ 
944+ ``` julia 
945+ #  Old: Library path computed in __init__()
946+ libmylib_path =  " " 
947+ 
948+ function  __init__ (
949+     #  Loads library on startup, whether it is used or not
950+     global  libmylib_path =  find_library ([" libmylib" 
951+ end 
952+ 
953+ function  myfunc (x)
954+     ccall ((:cfunc , libmylib_path), Cint, (Cint,), x)
955+ end 
956+ ``` 
957+ 
958+ New pattern using `LazyLibrary`: 
959+ 
960+ ``` julia 
961+ #  New: Library as const, no __init__() needed
962+ const  libmylib =  LazyLibrary (" libmylib" 
963+ 
964+ function  myfunc (x)
965+     #  Library loads automatically just before calling `cfunc`
966+     @ccall  libmylib. cfunc (x:: Cint ):: Cint 
967+ end 
968+ ``` 
969+ 
970+ For more details, see the [`Libdl.LazyLibrary`](@ref) documentation. 
971+ 
972+ ### Overloading `dlopen` for Custom Types 
973+ 
974+ The runtime will call `dlsym(:function, dlopen(library)::Ptr{Cvoid})` when a `@ccall` is executed. 
975+ The `Libdl.dlopen` function can be overloaded for custom types to provide alternate behaviors. 
976+ However, it is assumed that the library location and handle does not change 
977+ once it is determined, so the result of the call may be cached and reused. 
978+ Therefore, the number of times the `dlopen` expression executes is unspecified, 
979+ and returning different values for multiple calls will results in unspecified 
980+ (but valid) behavior. 
981+ 
982+ ### Computed Function Names 
983+ 
984+ If even more flexibility is needed, it is possible to use computed values as 
985+ function names by staging through [`eval`](@ref) as follows: 
869986
870987``` julia 
871988@eval  @ccall  " lib" $ (string (" a" " b" :: Cint 
@@ -876,38 +993,37 @@ expression, which is then evaluated. Keep in mind that `eval` only operates at t
876993so within this expression local variables will not be available (unless their values are substituted 
877994with `$`). For this reason, `eval` is typically only used to form top-level definitions, for example 
878995when wrapping libraries that contain many similar functions. 
879- A similar example can be constructed for [ ` @cfunction ` ] ( @ref ) .
880- 
881- However, doing this will also be very slow and leak memory, so you should usually avoid this and instead keep
882- reading.
883- The next section discusses how to use indirect calls to efficiently achieve a similar effect.
884996
885- ## Indirect Calls  
997+ ###  Indirect Calls 
886998
887- The first argument to ` @ccall `  can also be an expression evaluated at run time. In this
888- case, the expression must evaluate to a ` Ptr ` , which will be used as the address of the native
889- function to call. This behavior occurs when the first ` @ccall `  argument contains references
890- to non-constants, such as local variables, function arguments, or non-constant globals.
999+ The first argument to `@ccall` can also be an expression to be evaluated at run 
1000+ time, each time it is used. In this case, the expression must evaluate to a 
1001+ `Ptr`, which will be used as the address of the native function to call. This 
1002+ behavior occurs when the first `@ccall` argument is marked with `$` and when 
1003+ the first `ccall` argument is not a simple constant literal or expression in 
1004+ `()`. The argument can be any expression and can use local variables and 
1005+ arguments and can return a different value every time. 
8911006
892- For example, you might look up the function via ` dlsym ` ,
893- then cache it in a shared reference for that session. For example:
1007+ For example, you might implement a macro similar to `cglobal` that looks up the 
1008+ function via `dlsym`, then caches the pointer in a shared reference (which is 
1009+ auto reset to C_NULL during precompile saving). 
1010+ For example: 
8941011
8951012``` julia 
8961013macro  dlsym (lib, func)
897-     z =  Ref {Ptr{Cvoid}} (C_NULL )
1014+     z =  Ref (C_NULL )
8981015    quote 
899-         let  zlocal =  $ z[]
900-             if  zlocal ==  C_NULL 
901-                 zlocal =  dlsym ($ (esc (lib)):: Ptr{Cvoid} , $ (esc (func))):: Ptr{Cvoid} 
902-                 $ z[] =  zlocal
903-             end 
904-             zlocal
1016+         local  zlocal =  $ z[]
1017+         if  zlocal ==  C_NULL 
1018+             zlocal =  dlsym ($ (esc (lib)):: Ptr{Cvoid} , $ (esc (func))):: Ptr{Cvoid} 
1019+             $ z[] =  zlocal
9051020        end 
1021+         zlocal
9061022    end 
9071023end 
9081024
909- mylibvar =  Libdl . dlopen (" mylib" 
910- @ccall  $ (@dlsym (mylibvar, " myfunc" :: Cvoid 
1025+ const   mylibvar =  LazyLibrary (" mylib" 
1026+ @ccall  $ (@dlsym (dlopen ( mylibvar) , " myfunc" :: Cvoid 
9111027``` 
9121028
9131029## Closure cfunctions 
0 commit comments