Skip to content

Commit eb8c868

Browse files
authored
revamp Info object (#258)
* Make MPI.Info a subtype of AbstractDict * Allocated buffer correctly for strings
2 parents 97efe19 + 256749c commit eb8c868

File tree

8 files changed

+206
-69
lines changed

8 files changed

+206
-69
lines changed

deps/build.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ MPI_constants = [
6060
"MPI_REQUEST_NULL",
6161

6262
# info
63+
"MPI_MAX_INFO_KEY",
64+
"MPI_MAX_INFO_VAL",
6365
"MPI_INFO_NULL",
6466

6567
# proc
@@ -120,6 +122,8 @@ MPI_functions = [
120122
"MPI_INFO_DELETE",
121123
"MPI_INFO_FREE",
122124
"MPI_INFO_GET",
125+
"MPI_INFO_GET_NKEYS",
126+
"MPI_INFO_GET_NTHKEY",
123127
"MPI_INFO_GET_VALUELEN",
124128
"MPI_INFO_SET",
125129
"MPI_INIT",

docs/src/functions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,11 @@ MPI.Put
204204
MPI.Fetch_and_op
205205
MPI.Accumulate
206206
MPI.Get_accumulate
207+
```
208+
209+
# Info objects
210+
211+
```@docs
212+
MPI.Info
213+
MPI.infoval
207214
```

src/MPI.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ include(depfile)
1919

2020
include("mpi-base.jl")
2121
include("cman.jl")
22+
include("deprecated.jl")
2223

2324
const mpitype_dict = Dict{DataType, Cint}()
2425
const mpitype_dict_inverse = Dict{Cint, DataType}()

src/deprecated.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Base: @deprecate
2+
3+
# to be deprecated
4+
const FINALIZE_ATEXIT = Ref(true)
5+
@deprecate finalize_atexit() true false
6+
7+
@deprecate Info(::Cint) Info() false
8+
@deprecate Info_set(info::Info,key::AbstractString,value::AbstractString) info[Symbol(key)] = value false
9+
@deprecate Info_get(info::Info,key::AbstractString) info[Symbol(key)] false
10+
@deprecate Info_delete(info::Info,key::AbstractString) delete!(info, Symbol(key)) false
11+
@deprecate Info_free(info::Info) free(info) false
12+
13+

src/mpi-base.jl

Lines changed: 3 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -129,62 +129,7 @@ mutable struct Request
129129
end
130130
const REQUEST_NULL = Request(MPI_REQUEST_NULL, nothing)
131131

132-
mutable struct Info
133-
val::Cint
134-
function Info()
135-
newinfo = Ref{Cint}()
136-
ccall(MPI_INFO_CREATE, Nothing, (Ptr{Cint}, Ref{Cint}), newinfo, 0)
137-
info = new(newinfo[])
138-
refcount_inc()
139-
finalizer(info) do x
140-
ccall(MPI_INFO_FREE, Nothing, (Ref{Cint}, Ref{Cint}), x.val, 0)
141-
x.val = MPI_INFO_NULL
142-
refcount_dec()
143-
end
144-
info
145-
end
146-
147-
function Info(val::Cint)
148-
if val != MPI_INFO_NULL
149-
error("Info can only be created using Info()")
150-
end
151-
return new(MPI_INFO_NULL)
152-
end
153-
end
154-
const INFO_NULL = Info(MPI_INFO_NULL)
155-
156-
# the info functions assume that Fortran hidden arguments are placed at the end of the argument list
157-
function Info_set(info::Info,key::AbstractString,value::AbstractString)
158-
@assert isascii(key) && isascii(value)
159-
ccall(MPI_INFO_SET, Nothing,
160-
(Ref{Cint}, Ptr{UInt8}, Ptr{UInt8}, Ref{Cint}, Csize_t, Csize_t),
161-
info.val, key, value, 0, sizeof(key), sizeof(value))
162-
end
163-
164-
function Info_get(info::Info,key::AbstractString)
165-
@assert isascii(key)
166-
keyexists=Ref{Bool}()
167-
len=Ref{Cint}()
168-
ccall(MPI_INFO_GET_VALUELEN, Nothing,
169-
(Ref{Cint}, Ptr{UInt8}, Ptr{Cint}, Ptr{Bool}, Ref{Cint}, Csize_t),
170-
info.val, key, len, keyexists, 0, sizeof(key))
171-
if keyexists[]
172-
value=" "^(len[])
173-
ccall(MPI_INFO_GET, Nothing,
174-
(Ref{Cint}, Ptr{UInt8}, Ptr{Cint}, Ptr{UInt8}, Ptr{Bool}, Ref{Cint}, Csize_t, Csize_t),
175-
info.val, key, len, value, keyexists, 0, sizeof(key), sizeof(value))
176-
else
177-
value=""
178-
end
179-
value
180-
end
181-
182-
function Info_delete(info::Info,key::AbstractString)
183-
@assert isascii(key)
184-
ccall(MPI_INFO_DELETE, Nothing,
185-
(Ref{Cint}, Ptr{UInt8}, Ref{Cint}, Csize_t), info.val, key, 0, sizeof(key))
186-
end
187-
Info_free(info::Info) = finalize(info)
132+
include("mpi-info.jl")
188133

189134
mutable struct Status
190135
val::Array{Cint,1}
@@ -310,10 +255,6 @@ function _Finalize()
310255
ccall(MPI_FINALIZE, Nothing, (Ref{Cint},), 0)
311256
end
312257

313-
# to be deprecated
314-
const FINALIZE_ATEXIT = Ref(true)
315-
Base.@deprecate finalize_atexit() true
316-
317258
"""
318259
Abort(comm::Comm, errcode::Integer)
319260
@@ -398,11 +339,11 @@ function Comm_split(comm::Comm,color::Integer,key::Integer)
398339
MPI.Comm(newcomm[])
399340
end
400341

401-
function Comm_split_type(comm::Comm,split_type::Integer,key::Integer;info::Info=INFO_NULL)
342+
function Comm_split_type(comm::Comm,split_type::Integer,key::Integer; kwargs...)
402343
newcomm = Ref{Cint}()
403344
ccall(MPI_COMM_SPLIT_TYPE, Nothing,
404345
(Ref{Cint}, Ref{Cint}, Ref{Cint}, Ref{Cint}, Ptr{Cint}, Ref{Cint}),
405-
comm.val, split_type, key, info.val, newcomm, 0)
346+
comm.val, split_type, key, Info(kwargs...), newcomm, 0)
406347
MPI.Comm(newcomm[])
407348
end
408349

src/mpi-info.jl

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
"""
2+
Info <: AbstractDict{Symbol,String}
3+
4+
`MPI.Info` objects store key-value pairs, and are typically used for passing optional
5+
arguments to MPI functions.
6+
7+
# Usage
8+
9+
These will typically be hidden from user-facing APIs by splatting keywords, e.g.
10+
```julia
11+
function f(args...; kwargs...)
12+
info = Info(kwargs...)
13+
# pass `info` object to `ccall`
14+
end
15+
```
16+
17+
For manual usage, `Info` objects act like Julia `Dict` objects:
18+
```julia
19+
info = Info(init=true) # keyword argument is required
20+
info[key] = value
21+
x = info[key]
22+
delete!(info, key)
23+
```
24+
25+
If `init=false` is used in the costructor (the default), a "null" `Info` object will be
26+
returned: no keys can be added to such an object.
27+
"""
28+
mutable struct Info <: AbstractDict{Symbol,String}
29+
val::Cint
30+
function Info(;init=false)
31+
info = new(MPI_INFO_NULL)
32+
if init
33+
ccall(MPI_INFO_CREATE, Nothing, (Ptr{Cint}, Ref{Cint}), info, 0)
34+
refcount_inc()
35+
finalizer(free, info)
36+
end
37+
return info
38+
end
39+
end
40+
41+
# allows us to pass Info objects directly into Ptr{Cint} ccall signatures
42+
function Base.unsafe_convert(::Type{Ptr{Cint}}, info::Info)
43+
convert(Ptr{Cint}, pointer_from_objref(info))
44+
end
45+
46+
function free(info::Info)
47+
if info.val != MPI_INFO_NULL
48+
ccall(MPI_INFO_FREE, Nothing, (Ptr{Cint}, Ref{Cint}), info, 0)
49+
refcount_dec()
50+
end
51+
return nothing
52+
end
53+
54+
const INFO_NULL = Info(init=false)
55+
56+
# the info functions assume that Fortran hidden arguments are placed at the end of the argument list
57+
function Base.setindex!(info::Info, value::AbstractString, key::Symbol)
58+
skey = String(key)
59+
@assert isascii(skey) && isascii(value) &&
60+
length(skey) <= MPI_MAX_INFO_KEY && length(value) <= MPI_MAX_INFO_VAL
61+
ccall(MPI_INFO_SET, Nothing,
62+
(Ptr{Cint}, Ptr{UInt8}, Ptr{UInt8}, Ref{Cint}, Csize_t, Csize_t),
63+
info, skey, value, 0, sizeof(skey), sizeof(value))
64+
end
65+
66+
Base.setindex!(info::Info, value::Any, key::Symbol) = info[key] = infoval(value)
67+
68+
"""
69+
infoval(x)
70+
71+
Convert Julia object `x` to a string representation for storing in an [`Info`](@ref) object.
72+
73+
The MPI specification allows passing strings, Boolean values, integers, and lists.
74+
"""
75+
infoval(x::String) = x
76+
infoval(x::Bool) = string(x) # "true"/"false"
77+
infoval(x::Integer) = string(x) # decimal values
78+
infoval(xs::AbstractVector) = join(map(infoval, xs), ',') # separated by commas
79+
infoval(xs::Tuple) = join(map(infoval, xs), ',') # separated by commas
80+
81+
function Info(kvs::Pair...)
82+
info = Info(;init=true)
83+
for (k,v) in kvs
84+
info[k] = v
85+
end
86+
return info
87+
end
88+
89+
90+
function Base.getindex(info::Info, key::Symbol)
91+
skey = String(key)
92+
@assert isascii(skey) && length(skey) <= MPI_MAX_INFO_KEY
93+
keyexists = Ref{Cint}()
94+
len = Ref{Cint}()
95+
ccall(MPI_INFO_GET_VALUELEN, Nothing,
96+
(Ptr{Cint}, Ptr{UInt8}, Ptr{Cint}, Ptr{Cint}, Ref{Cint}, Csize_t),
97+
info, skey, len, keyexists, 0, sizeof(skey))
98+
99+
if keyexists[] == 0
100+
throw(KeyError(key))
101+
end
102+
103+
buffer = Vector{UInt8}(undef, len[])
104+
ccall(MPI_INFO_GET, Nothing,
105+
(Ptr{Cint}, Ptr{UInt8}, Ptr{Cint}, Ptr{UInt8}, Ptr{Cint}, Ref{Cint}, Csize_t, Csize_t),
106+
info, skey, len, buffer, keyexists, 0, sizeof(skey), sizeof(buffer))
107+
return String(buffer)
108+
end
109+
110+
function Base.delete!(info::Info,key::Symbol)
111+
skey = String(key)
112+
@assert isascii(skey) && length(skey) <= MPI_MAX_INFO_KEY
113+
ccall(MPI_INFO_DELETE, Nothing,
114+
(Ptr{Cint}, Ptr{UInt8}, Ref{Cint}, Csize_t),
115+
info, skey, 0, sizeof(skey))
116+
end
117+
118+
function Base.length(info::Info)
119+
if info.val == MPI_INFO_NULL
120+
return 0
121+
end
122+
nkeys = Ref{Cint}()
123+
ccall(MPI_INFO_GET_NKEYS, Nothing,
124+
(Ptr{Cint}, Ptr{Cint}, Ref{Cint}),
125+
info, nkeys, 0)
126+
return Int(nkeys[])
127+
end
128+
129+
function nthkey(info::Info, n::Integer)
130+
buffer = Vector{UInt8}(undef, MPI_MAX_INFO_KEY)
131+
ccall(MPI_INFO_GET_NTHKEY, Nothing,
132+
(Ptr{Cint}, Ref{Cint}, Ptr{UInt8}, Ref{Cint}, Csize_t),
133+
info, n, buffer, 0, length(buffer))
134+
i = findlast(!isequal(UInt8(' ')), buffer)
135+
resize!(buffer, i)
136+
Symbol(buffer)
137+
end
138+
139+
function Base.iterate(infokeys::Base.KeySet{Symbol,Info}, i=0)
140+
info = infokeys.dict
141+
if i < length(info)
142+
key = nthkey(info, i)
143+
return key, i+1
144+
else
145+
return nothing
146+
end
147+
end
148+
149+
function Base.iterate(info::Info, i=0)
150+
if i < length(info)
151+
key = nthkey(info, i)
152+
val = info[key]
153+
return key=>val, i+1
154+
else
155+
return nothing
156+
end
157+
end

src/win_mpiconstants.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const MPI_LOCK_EXCLUSIVE = Int32(234)
3232
const MPI_LOCK_SHARED = Int32(235)
3333
const MPI_LOR = Int32(1476395015)
3434
const MPI_LXOR = Int32(1476395017)
35+
const MPI_MAX_INFO_KEY = Int32(254)
36+
const MPI_MAX_INFO_VAL = Int32(1023)
3537
const MPI_MAX = Int32(1476395009)
3638
const MPI_MAXLOC = Int32(1476395020)
3739
const MPI_MIN = Int32(1476395010)
@@ -88,6 +90,8 @@ const MPI_INFO_CREATE = (:MPI_INFO_CREATE, libmpi)
8890
const MPI_INFO_DELETE = (:MPI_INFO_DELETE, libmpi)
8991
const MPI_INFO_FREE = (:MPI_INFO_FREE, libmpi)
9092
const MPI_INFO_GET = (:MPI_INFO_GET, libmpi)
93+
const MPI_INFO_GET_NKEYS = (:MPI_INFO_GET_NKEYS, libmpi)
94+
const MPI_INFO_GET_NTHKEY = (:MPI_INFO_GET_NTHKEY, libmpi)
9195
const MPI_INFO_GET_VALUELEN = (:MPI_INFO_GET_VALUELEN, libmpi)
9296
const MPI_INFO_SET = (:MPI_INFO_SET, libmpi)
9397
const MPI_INITIALIZED = (:MPI_INITIALIZED, libmpi)

test/test_info.jl

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ MPI.Init()
55

66
info = MPI.Info()
77
@test typeof(info) == MPI.Info
8-
@test info.val != MPI.Info
9-
MPI.Info_set(info, "foo", "fast")
10-
MPI.Info_set(info, "bar", "evenfaster")
11-
@test MPI.Info_get(info, "foo" ) == "fast"
12-
MPI.Info_delete(info, "bar")
13-
@test MPI.Info_get(info, "bar" ) == ""
14-
finalize(info)
8+
@test info.val == MPI.MPI_INFO_NULL
9+
10+
testinfo(;kwargs...) = MPI.Info(kwargs...)
11+
12+
info = testinfo(foo="fast", bar=true, baz=[10, -2])
13+
@test info.val != MPI.MPI_INFO_NULL
14+
15+
@test length(info) == 3
16+
@test info[:foo] == "fast"
17+
18+
@test sort!(collect(keys(info))) == [:bar, :baz, :foo]
19+
@test sort!(collect(info)) == [:bar=>"true", :baz=>"10,-2", :foo=>"fast"]
20+
21+
delete!(info, :bar)
22+
@test_throws KeyError info[:bar]
23+
24+
MPI.free(info)
1525
@test info.val == MPI.MPI_INFO_NULL
1626

1727
MPI.Finalize()

0 commit comments

Comments
 (0)