|
| 1 | +# External libraries and packages |
| 2 | + |
| 3 | +Other libraries and packages may also make use of MPI. There are several concerns to ensure things are set up correctly. |
| 4 | + |
| 5 | +## Binary requirements |
| 6 | + |
| 7 | +You need to ensure that external libraries are built correctly. In particular, if you are [using a system-provided MPI backend](@ref using_system_mpi) in Julia, you also need to use the *same* system-provided binary for all packages and external libraries you use. |
| 8 | + |
| 9 | +## Passing MPI handles via `ccall` |
| 10 | + |
| 11 | +When passing MPI.jl handle objects ([`MPI.Comm`](@ref), [`MPI.Info`](@ref), etc) to C/C++ functions [via `ccall`](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/), you should pass the object directly as an argument, and specify the argument type as either the underlying handle type (`MPI.MPI_Comm`, `MPI.MPI_Info`, etc.), or a pointer (`Ptr{MPI.MPI_Comm}`, `Ptr{MPI.MPI_Info}`, etc.). This will internally handle the unwrapping, but ensure that a reference is kept to avoid premature garbage collection. |
| 12 | + |
| 13 | +For example the C function signatures |
| 14 | +```C |
| 15 | +int cfunc1(MPI_Comm comm); |
| 16 | +int cfunc2(MPI_Comm * comm); |
| 17 | +``` |
| 18 | +would be called as |
| 19 | +```julia |
| 20 | +ccall((:cfunc1, lib), Cint, (MPI.MPI_Comm,), comm) |
| 21 | +ccall((:cfunc2, lib), Cint, (Ptr{MPI.MPI_Comm},), comm) |
| 22 | +``` |
| 23 | + |
| 24 | +## Object finalizers and `MPI.Finalize` |
| 25 | + |
| 26 | +External libraries may allocate their own MPI handles (e.g., create or duplicate MPI communictors), which need to be cleaned up before MPI is finalized. If these are attached to [object finalizers](https://docs.julialang.org/en/v1/base/base/#Base.finalizer), they may not be guaranteed to be called before `MPI.Finalize`, which can result in an error upon program exit. (By default, MPI.jl will install an [`atexit`](https://docs.julialang.org/en/v1/base/base/#Base.atexit) hook that calls `MPI.Finalize` if it hasn't already been invoked.) |
| 27 | + |
| 28 | +There are two typical solutions to this problem: |
| 29 | + |
| 30 | +1. Gate the clean up functions behind an [`MPI.Finalized`](@ref) call, e.g. |
| 31 | + |
| 32 | + ```julia |
| 33 | + finalizer(obj) do obj |
| 34 | + if !MPI.Finalized |
| 35 | + # call clean up function |
| 36 | + end |
| 37 | + end |
| 38 | + ``` |
| 39 | + |
| 40 | +2. Keep track of all such objects, clean them up via [`MPI.add_finalize_hook!`](@ref), e.g. |
| 41 | + |
| 42 | + ```julia |
| 43 | + finalizer(obj) do obj |
| 44 | + # call clean up function |
| 45 | + end |
| 46 | + MPI.add_finalize_hook!(() -> finalize(obj)) |
| 47 | + ``` |
| 48 | + A variant of this is to keep track of all such objects, for example, using a [`WeakKeyDict`](https://docs.julialang.org/en/v1/base/collections/#Base.WeakKeyDict), and use a hook to clean them all: |
| 49 | + ```julia |
| 50 | + const REFS = WeakKeyDict{ObjType, Nothing}() |
| 51 | + MPI.add_finalize_hook!() do |
| 52 | + for obj in keys(REFS) |
| 53 | + finalize(obj) |
| 54 | + end |
| 55 | + end |
| 56 | + |
| 57 | + # for each object `obj` |
| 58 | + finalizer(obj) do obj |
| 59 | + # call clean up function |
| 60 | + end |
| 61 | + REFS[obj] = nothing |
| 62 | + ``` |
0 commit comments