Skip to content

Commit 54cb06e

Browse files
authored
add nice init function (#485)
* add nice init function * make multiple Finalize safe * bump version * add error if trying to initialize after finalize
1 parent c659af7 commit 54cb06e

File tree

5 files changed

+70
-80
lines changed

5 files changed

+70
-80
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MPI"
22
uuid = "da04e1cc-30fd-572f-bb4f-1f8673147195"
33
authors = []
4-
version = "0.18.2"
4+
version = "0.19.0"
55

66
[deps]
77
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"

docs/src/environment.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ MPI.ThreadLevel
1818
```@docs
1919
MPI.Abort
2020
MPI.Init
21-
MPI.Init_thread
2221
MPI.Query_thread
2322
MPI.Is_thread_main
2423
MPI.Initialized

src/deprecated.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,7 @@ import Base: @deprecate
183183
Get_accumulate(view(origin_buffer,1:count), view(result_buffer,1:count), target_rank, target_disp, op, win), false)
184184
@deprecate(Get_accumulate(origin_buffer::Ref, result_buffer::Ref, count::Integer, target_rank::Integer, target_disp::Integer, op::Op, win::Win),
185185
Get_accumulate(origin_buffer, result_buffer, target_rank, target_disp, op, win), false)
186+
187+
188+
# Deprecated in v0.19
189+
@deprecate(Init_thread(required::ThreadLevel; kwargs...), Init(;threadlevel=required, kwargs...), false)

src/environment.jl

Lines changed: 64 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -51,48 +51,70 @@ function run_init_hooks()
5151
return nothing
5252
end
5353

54-
function _finalize()
55-
# MPI_Finalize is a collective and can act like a barrier (this may be implementation
56-
# specific). If we are terminating due to a Julia exception, we shouldn't call
57-
# MPI_Finalize. We thus peek at the current exception, and only if that field is
58-
# nothing do we terminate.
59-
if !Finalized() && ccall(:jl_current_exception, Any, ()) === nothing
60-
Finalize()
61-
end
62-
end
63-
64-
65-
6654

6755
"""
68-
Init(;finalize_atexit=true, errors_return=true)
56+
Init(;threadlevel=:serialized, finalize_atexit=true, errors_return=true)
6957
7058
Initialize MPI in the current process. The keyword options:
7159
72-
- `finalize_atexit`: if true, adds an `atexit` hook to call [`MPI.Finalize`](@ref) if it hasn't already been called.
73-
- `errors_return`: if true, will set the default error handlers for [`MPI.COMM_SELF`](@ref) and [`MPI.COMM_WORLD`](@ref) to be `MPI.ERRORS_RETURN`. MPI errors will appear as Julia exceptions.
60+
- `threadlevel`: either `:single`, `:funneled`, `:serialized` (default),
61+
`:multiple`, or an instance of [`ThreadLevel`](@ref).
62+
- `finalize_atexit`: if `true` (default), adds an `atexit` hook to call
63+
[`MPI.Finalize`](@ref) if it hasn't already been called.
64+
- `errors_return`: if `true` (default), will set the default error handlers for
65+
[`MPI.COMM_SELF`](@ref) and [`MPI.COMM_WORLD`](@ref) to be
66+
`MPI.ERRORS_RETURN`. MPI errors will then appear as Julia exceptions.
7467
75-
All MPI programs must contain exactly one call to `MPI.Init` or
76-
[`MPI.Init_thread`](@ref). In particular, note that it is not valid to call `MPI.Init` or
77-
`MPI.Init_thread` again after calling [`MPI.Finalize`](@ref).
68+
It will return the [`ThreadLevel`](@ref) value which MPI is initialized at.
7869
79-
The only MPI functions that may be called before `MPI.Init`/`MPI.Init_thread` are
70+
All MPI programs must call this function at least once before calling any other
71+
MPI operations: the only MPI functions that may be called before `MPI.Init` are
8072
[`MPI.Initialized`](@ref) and [`MPI.Finalized`](@ref).
8173
74+
It is safe to call `MPI.Init` multiple times, however it is not valid to call
75+
it after calling [`MPI.Finalize`](@ref).
76+
8277
# External links
8378
$(_doc_external("MPI_Init"))
79+
$(_doc_external("MPI_Init_thread"))
8480
"""
85-
function Init(;finalize_atexit=true, errors_return=true)
86-
@mpichk ccall((:MPI_Init, libmpi), Cint, (Ptr{Cint},Ptr{Cint}), C_NULL, C_NULL)
87-
if finalize_atexit
88-
atexit(_finalize)
81+
function Init(;threadlevel=:serialized, finalize_atexit=true, errors_return=true)
82+
if threadlevel isa Symbol
83+
threadlevel = ThreadLevel(threadlevel)
84+
end
85+
if MPI.Finalized()
86+
error("MPI cannot be initialized after MPI.Finalize has been called.")
8987
end
88+
if MPI.Initialized()
89+
provided = Query_thread()
90+
if provided < threadlevel
91+
@warn "MPI already initialized with thread level $provided, requested = $threadlevel"
92+
end
93+
else
94+
provided = _init_thread(threadlevel)
95+
if provided < threadlevel
96+
@warn "MPI thread level requested = $required, provided = $provided"
97+
end
98+
99+
if finalize_atexit
100+
atexit() do
101+
# MPI_Finalize is a collective and can act like a barrier (this may be implementation
102+
# specific). If we are terminating due to a Julia exception, we shouldn't call
103+
# MPI_Finalize. We thus peek at the current exception, and only if that field is
104+
# nothing do we terminate.
105+
if !Finalized() && ccall(:jl_current_exception, Any, ()) === nothing
106+
Finalize()
107+
end
108+
end
109+
end
90110

91-
run_init_hooks()
92-
if errors_return
93-
set_default_error_handler_return()
111+
run_init_hooks()
112+
if errors_return
113+
set_default_error_handler_return()
114+
end
115+
_warn_if_wrong_mpi()
94116
end
95-
_warn_if_wrong_mpi()
117+
return provided
96118
end
97119

98120
"""
@@ -121,60 +143,23 @@ An Enum denoting the level of threading support in the current process:
121143
THREAD_SERIALIZED = MPI_THREAD_SERIALIZED
122144
THREAD_MULTIPLE = MPI_THREAD_MULTIPLE
123145
end
124-
125-
126-
"""
127-
Init_thread(required::ThreadLevel; finalize_atexit=true, errors_return=true)
128-
129-
Initialize MPI and the MPI thread environment in the current process. The argument specifies the required level of threading
130-
support, see [`ThreadLevel`](@ref).
131-
132-
The keyword options are:
133-
134-
- `finalize_atexit`: if true, adds an `atexit` hook to call [`MPI.Finalize`](@ref) if it hasn't already been called.
135-
- `errors_return`: if true, will set the default error handlers for [`MPI.COMM_SELF`](@ref) and [`MPI.COMM_WORLD`](@ref) to be `MPI.ERRORS_RETURN`. MPI errors will appear as Julia exceptions.
136-
137-
138-
The function will return the provided `ThreadLevel`, and values may be compared via
139-
inequalities, i.e.
140-
141-
```julia
142-
provided = Init_thread(required)
143-
@assert provided >= required
144-
```
145-
146-
All MPI programs must contain exactly one call to [`MPI.Init`](@ref) or
147-
`MPI.Init_thread`. In particular, note that it is not valid to call `MPI.Init` or
148-
`MPI.Init_thread` again after calling [`MPI.Finalize`](@ref).
149-
150-
The only MPI functions that may be called before `MPI.Init`/`MPI.Init_thread` are
151-
[`MPI.Initialized`](@ref) and [`MPI.Finalized`](@ref).
152-
153-
# External links
154-
$(_doc_external("MPI_Init_thread"))
155-
"""
156-
function Init_thread(required::ThreadLevel; finalize_atexit=true, errors_return=true)
146+
ThreadLevel(threadlevel::Symbol) =
147+
threadlevel == :single ? THREAD_SINGLE :
148+
threadlevel == :funneled ? THREAD_FUNNELED :
149+
threadlevel == :serialized ? THREAD_SERIALIZED :
150+
threadlevel == :multiple ? THREAD_MULTIPLE :
151+
error("Invalid threadlevel: must be one of :single, :funneled, :serialized, or :multiple")
152+
153+
function _init_thread(required::ThreadLevel)
157154
r_provided = Ref{ThreadLevel}()
158155
# int MPI_Init_thread(int *argc, char ***argv, int required, int *provided)
159156
@mpichk ccall((:MPI_Init_thread, libmpi), Cint,
160-
(Ptr{Cint},Ptr{Cvoid}, ThreadLevel, Ref{ThreadLevel}),
161-
C_NULL, C_NULL, required, r_provided)
162-
provided = r_provided[]
163-
if provided < required
164-
@warn "Thread level requested = $required, provided = $provided"
165-
end
166-
167-
if finalize_atexit
168-
atexit(_finalize)
169-
end
170-
run_init_hooks()
171-
if errors_return
172-
set_default_error_handler_return()
173-
end
174-
_warn_if_wrong_mpi()
175-
return provided
157+
(Ptr{Cint},Ptr{Cvoid}, ThreadLevel, Ref{ThreadLevel}),
158+
C_NULL, C_NULL, required, r_provided)
159+
return r_provided[]
176160
end
177161

162+
178163
"""
179164
Query_thread()
180165
@@ -226,7 +211,9 @@ call this function when Julia exits, if it hasn't already been called.
226211
$(_doc_external("MPI_Finalize"))
227212
"""
228213
function Finalize()
229-
@mpichk ccall((:MPI_Finalize, libmpi), Cint, ())
214+
if !MPI.Finalized()
215+
@mpichk ccall((:MPI_Finalize, libmpi), Cint, ())
216+
end
230217
return nothing
231218
end
232219

test/test_threads.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ else
88
ArrayType = Array
99
end
1010

11-
provided = MPI.Init_thread(MPI.THREAD_MULTIPLE)
11+
provided = MPI.Init(threadlevel=:multiple)
1212

1313
@test MPI.THREAD_SINGLE <= provided <= MPI.THREAD_MULTIPLE
1414
@test MPI.Query_thread() == provided

0 commit comments

Comments
 (0)