Skip to content

Commit 92bc4ac

Browse files
simonbyrnesloedegiordano
authored
Add section on external libraries (#713)
Fixes #702 Co-authored-by: Michael Schlottke-Lakemper <michael@sloede.com> Co-authored-by: Mosè Giordano <giordano@users.noreply.github.com>
1 parent 69ce0bf commit 92bc4ac

File tree

6 files changed

+80
-5
lines changed

6 files changed

+80
-5
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ makedocs(
5757
"index.md",
5858
"configuration.md",
5959
"usage.md",
60+
"external.md",
6061
"knownissues.md",
6162
"Examples" => EXAMPLES,
6263
"Reference" => [

docs/src/configuration.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ julia --project -e 'using Pkg; Pkg.add("MPIPreferences")'
2323
See [Migration from MPI.jl v0.19 or earlier](@ref) for more information on
2424
how to migrate your configuration from earlier MPI.jl versions.
2525

26-
## Using a system-provided MPI backend
26+
## [Using a system-provided MPI backend](@id using_system_mpi)
2727

2828
### Requirements
2929

@@ -206,5 +206,3 @@ unknown MPI ABIs is not supported anymore. See also
206206
Removed without replacement. Automatic generation of a constants file for
207207
unknown MPI ABIs is not supported anymore. See also
208208
[#574](https://github.com/JuliaParallel/MPI.jl/issues/574).
209-
210-

docs/src/external.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
```

docs/src/reference/comm.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ each process a unique *rank* (see [`MPI.Comm_rank`](@ref)) taking an integer val
1212
MPI.Comm
1313
```
1414

15-
If you need to pass a Julia `MPI.Comm` object to an external C/C++ library ([via `ccall`](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/)) that expects an `MPI_Comm` argument, you should declare the corresponding `ccall` argument type `MPI.API.MPI_Comm` and the correct conversion will be performed.
16-
1715
## Constants
1816

1917
```@docs

docs/src/reference/environment.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ MPI.Is_thread_main
2323
MPI.Initialized
2424
MPI.Finalize
2525
MPI.Finalized
26+
MPI.add_init_hook!
27+
MPI.add_finalize_hook!
2628
```
2729

2830
## Errors

src/environment.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ end
4242

4343

4444
const mpi_init_hooks = Any[]
45+
46+
"""
47+
MPI.add_init_hook!(f)
48+
49+
Register a function `f` that will be called as `f()` when `MPI.Init` is
50+
called. These are invoked in a first-in, first-out (FIFO) order.
51+
"""
4552
add_init_hook!(f) = push!(mpi_init_hooks, f)
4653
function run_init_hooks()
4754
while !isempty(mpi_init_hooks)
@@ -52,6 +59,13 @@ function run_init_hooks()
5259
end
5360

5461
const mpi_finalize_hooks = Any[]
62+
63+
"""
64+
MPI.add_finalize_hook!(f)
65+
66+
Register a function `f` that will be called as `f()` when `MPI.Finalizer` is
67+
called. These are invoked in a last-in, first-out (LIFO) order.
68+
"""
5569
add_finalize_hook!(f) = push!(mpi_finalize_hooks, f)
5670
function run_finalize_hooks()
5771
while !isempty(mpi_finalize_hooks)

0 commit comments

Comments
 (0)