<commit_before>mutable struct Win
    val::MPI_Win
    object
end
Base.:(==)(a::Win, b::Win) = a.val == b.val
Base.cconvert(::Type{MPI_Win}, win::Win) = win
Base.unsafe_convert(::Type{MPI_Win}, win::Win) = win.val
Base.unsafe_convert(::Type{Ptr{MPI_Win}}, win::Win) = convert(Ptr{MPI_Win}, pointer_from_objref(win))

const WIN_NULL = Win(API.MPI_WIN_NULL[], nothing)
add_load_time_hook!(() -> WIN_NULL.val = API.MPI_WIN_NULL[])

Win() = Win(WIN_NULL.val, nothing)

function free(win::Win)
    if win != WIN_NULL && !Finalized()
        # int MPI_Win_free(MPI_Win *win)
        API.MPI_Win_free(win)
    end
    win.object = nothing
    return nothing
end

mutable struct LockType
    val::Cint
end
Base.:(==)(a::LockType, b::LockType) = a.val == b.val
Base.cconvert(::Type{Cint}, lock_type::LockType) = lock_type
Base.unsafe_convert(::Type{Cint}, lock_type::LockType) = lock_type.val
Base.unsafe_convert(::Type{Ptr{Cint}}, lock_type::LockType) = convert(Ptr{Cint}, pointer_from_objref(lock_type))

const LOCK_EXCLUSIVE = LockType(API.MPI_LOCK_EXCLUSIVE[])
const LOCK_SHARED    = LockType(API.MPI_LOCK_SHARED[]   )
add_load_time_hook!(() -> LOCK_EXCLUSIVE.val = API.MPI_LOCK_EXCLUSIVE[])
add_load_time_hook!(() -> LOCK_SHARED.val    = API.MPI_LOCK_SHARED[]   )
LockType(sym::Symbol) =
    sym ≡ :exclusive ? LOCK_EXCLUSIVE :
    sym ≡ :shared ? LOCK_SHARED :
    error("Invalid LockType $sym")

"""
    MPI.Win_create(base[, size::Integer, disp_unit::Integer], comm::Comm; infokws...)

Create a window over the array `base`, returning a `Win` object used by these
processes to perform RMA operations. This is a collective call over `comm`.

- `size` is the size of the window in bytes (default = `sizeof(base)`)
- `disp_unit` is the size of address scaling in bytes (default =
  `sizeof(eltype(base))`)
- `infokws` are info keys providing optimization hints to the runtime.

[`MPI.free`](@ref) should be called on the `Win` object once operations have
been completed.
"""
function Win_create(base, size::Integer, disp_unit::Integer, comm::Comm; infokws...)
    win = Win()
    # int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, MPI_Info info,
    #                    MPI_Comm comm, MPI_Win *win)
    API.MPI_Win_create(base, size, disp_unit, Info(infokws...), comm, win)
    win.object = base
    finalizer(free, win)
    return win
end
function Win_create(base::Array{T}, comm::Comm; infokws...) where T
    Win_create(base, sizeof(base), sizeof(T), comm; infokws...)
end
function Win_create(base::SubArray{T}, comm::Comm; infokws...) where T
    Base.iscontiguous(base) || error("Array must be contiguous")
    Win_create(base, sizeof(base), sizeof(T), comm; infokws...)
end

"""
    win, array = MPI.Win_allocate_shared(Array{T}, dims, comm::Comm; infokws...)

Create and allocate a shared memory window for objects of type `T` of dimension
`dims` (either an integer or tuple of integers), returning a `Win` and the
`Array{T}` attached to the local process.

This is a collective call over `comm`, but `dims` can differ for each call (and
can be zero).

Use [`MPI.Win_shared_query`](@ref) to obtain the `Array` attached to a different
process in the same shared memory space.

`infokws` are info keys providing optimization hints.

[`MPI.free`](@ref) should be called on the `Win` object once operations have
been completed.
"""
function Win_allocate_shared(::Type{Ptr{T}}, len::Integer, comm::Comm; kwargs...) where T
    win = Win()
    out_baseptr = Ref{Ptr{T}}()
    # int MPI_Win_allocate_shared(MPI_Aint size, int disp_unit, MPI_Info info,
    #                             MPI_Comm comm, void *baseptr, MPI_Win *win)
    API.MPI_Win_allocate_shared(len*sizeof(T), sizeof(T), Info(kwargs...), comm, out_baseptr, win)
    finalizer(free, win)
    return win, out_baseptr[]
end
function Win_allocate_shared(::Type{Array{T}}, dims, comm::Comm; kwargs...) where T
    win, ptr = Win_allocate_shared(Ptr{T}, prod(dims), comm; kwargs...)
    array = unsafe_wrap(Array, ptr, dims)
    win.object = array
    finalizer(free, win)
    return win, array
end

"""
    array = Win_shared_query(Array{T}, [dims,] win; rank)

Obtain the shared memory allocated by [`Win_allocate_shared`](@ref) of the
process `rank` in `win`. Returns an `Array{T}` of size `dims` (being a
`Vector{T}` if no `dims` argument is provided).
"""
Win_shared_query(::Type{Array{T}}, win::Win; rank) where {T} =
    Win_shared_query(Array{T}, win, rank)
Win_shared_query(::Type{Ptr{T}}, win::Win; rank) where {T} =
    Win_shared_query(Ptr{T}, win, rank)
Win_shared_query(::Type{Array{T}}, dims, win::Win; rank) where {T} =
    Win_shared_query(Array{T}, dims, win, rank)

function Win_shared_query(::Type{Ptr{T}}, win::Win, owner_rank::Integer) where T
    out_len = Ref{Cptrdiff_t}()
    out_sizeT = Ref{Cint}()
    out_baseptr = Ref{Ptr{T}}()
    # int MPI_Win_shared_query(MPI_Win win, int rank, MPI_Aint *size,
    #                          int *disp_unit, void *baseptr)
    API.MPI_Win_shared_query(win, owner_rank, out_len, out_sizeT, out_baseptr)
    out_len[], out_sizeT[], out_baseptr[]
end
function Win_shared_query(::Type{Array{T}}, win::Win, owner_rank::Integer) where T
    len, sizeT, ptr = Win_shared_query(Ptr{T}, win, owner_rank)
    sizeT == sizeof(T) || error("type sizes don't match")
    return unsafe_wrap(Array, ptr, div(len, sizeT))
end
Win_shared_query(::Type{Array{T}}, dims, win::Win, owner_rank::Integer) where T =
    reshape(Win_shared_query(Array{T}, win, owner_rank), dims)


"""
    MPI.Win_create_dynamic(comm::Comm; infokws...)

Create a dynamic window returning a `Win` object used by these processes to perform RMA operations
<commit_msg>check for length 0 in Win_shared_query (#678)<commit_after>mutable struct Win
    val::MPI_Win
    object
end
Base.:(==)(a::Win, b::Win) = a.val == b.val
Base.cconvert(::Type{MPI_Win}, win::Win) = win
Base.unsafe_convert(::Type{MPI_Win}, win::Win) = win.val
Base.unsafe_convert(::Type{Ptr{MPI_Win}}, win::Win) = convert(Ptr{MPI_Win}, pointer_from_objref(win))

const WIN_NULL = Win(API.MPI_WIN_NULL[], nothing)
add_load_time_hook!(() -> WIN_NULL.val = API.MPI_WIN_NULL[])

Win() = Win(WIN_NULL.val, nothing)

function free(win::Win)
    if win != WIN_NULL && !Finalized()
        # int MPI_Win_free(MPI_Win *win)
        API.MPI_Win_free(win)
    end
    win.object = nothing
    return nothing
end

mutable struct LockType
    val::Cint
end
Base.:(==)(a::LockType, b::LockType) = a.val == b.val
Base.cconvert(::Type{Cint}, lock_type::LockType) = lock_type
Base.unsafe_convert(::Type{Cint}, lock_type::LockType) = lock_type.val
Base.unsafe_convert(::Type{Ptr{Cint}}, lock_type::LockType) = convert(Ptr{Cint}, pointer_from_objref(lock_type))

const LOCK_EXCLUSIVE = LockType(API.MPI_LOCK_EXCLUSIVE[])
const LOCK_SHARED    = LockType(API.MPI_LOCK_SHARED[]   )
add_load_time_hook!(() -> LOCK_EXCLUSIVE.val = API.MPI_LOCK_EXCLUSIVE[])
add_load_time_hook!(() -> LOCK_SHARED.val    = API.MPI_LOCK_SHARED[]   )
LockType(sym::Symbol) =
    sym ≡ :exclusive ? LOCK_EXCLUSIVE :
    sym ≡ :shared ? LOCK_SHARED :
    error("Invalid LockType $sym")

"""
    MPI.Win_create(base[, size::Integer, disp_unit::Integer], comm::Comm; infokws...)

Create a window over the array `base`, returning a `Win` object used by these
processes to perform RMA operations. This is a collective call over `comm`.

- `size` is the size of the window in bytes (default = `sizeof(base)`)
- `disp_unit` is the size of address scaling in bytes (default =
  `sizeof(eltype(base))`)
- `infokws` are info keys providing optimization hints to the runtime.

[`MPI.free`](@ref) should be called on the `Win` object once operations have
been completed.
"""
function Win_create(base, size::Integer, disp_unit::Integer, comm::Comm; infokws...)
    win = Win()
    # int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, MPI_Info info,
    #                    MPI_Comm comm, MPI_Win *win)
    API.MPI_Win_create(base, size, disp_unit, Info(infokws...), comm, win)
    win.object = base
    finalizer(free, win)
    return win
end
function Win_create(base::Array{T}, comm::Comm; infokws...) where T
    Win_create(base, sizeof(base), sizeof(T), comm; infokws...)
end
function Win_create(base::SubArray{T}, comm::Comm; infokws...) where T
    Base.iscontiguous(base) || error("Array must be contiguous")
    Win_create(base, sizeof(base), sizeof(T), comm; infokws...)
end

"""
    win, array = MPI.Win_allocate_shared(Array{T}, dims, comm::Comm; infokws...)

Create and allocate a shared memory window for objects of type `T` of dimension
`dims` (either an integer or tuple of integers), returning a `Win` and the
`Array{T}` attached to the local process.

This is a collective call over `comm`, but `dims` can differ for each call (and
can be zero).

Use [`MPI.Win_shared_query`](@ref) to obtain the `Array` attached to a different
process in the same shared memory space.

`infokws` are info keys providing optimization hints.

[`MPI.free`](@ref) should be called on the `Win` object once operations have
been completed.
"""
function Win_allocate_shared(::Type{Ptr{T}}, len::Integer, comm::Comm; kwargs...) where T
    win = Win()
    out_baseptr = Ref{Ptr{T}}()
    # int MPI_Win_allocate_shared(MPI_Aint size, int disp_unit, MPI_Info info,
    #                             MPI_Comm comm, void *baseptr, MPI_Win *win)
    API.MPI_Win_allocate_shared(len*sizeof(T), sizeof(T), Info(kwargs...), comm, out_baseptr, win)
    finalizer(free, win)
    return win, out_baseptr[]
end
function Win_allocate_shared(::Type{Array{T}}, dims, comm::Comm; kwargs...) where T
    win, ptr = Win_allocate_shared(Ptr{T}, prod(dims), comm; kwargs...)
    array = unsafe_wrap(Array, ptr, dims)
    win.object = array
    finalizer(free, win)
    return win, array
end

"""
    array = Win_shared_query(Array{T}, [dims,] win; rank)

Obtain the shared memory allocated by [`Win_allocate_shared`](@ref) of the
process `rank` in `win`. Returns an `Array{T}` of size `dims` (being a
`Vector{T}` if no `dims` argument is provided).
"""
Win_shared_query(::Type{Array{T}}, win::Win; rank) where {T} =
    Win_shared_query(Array{T}, win, rank)
Win_shared_query(::Type{Ptr{T}}, win::Win; rank) where {T} =
    Win_shared_query(Ptr{T}, win, rank)
Win_shared_query(::Type{Array{T}}, dims, win::Win; rank) where {T} =
    Win_shared_query(Array{T}, dims, win, rank)

function Win_shared_query(::Type{Ptr{T}}, win::Win, owner_rank::Integer) where T
    out_len = Ref{Cptrdiff_t}()
    out_sizeT = Ref{Cint}()
    out_baseptr = Ref{Ptr{T}}()
    # int MPI_Win_shared_query(MPI_Win win, int rank, MPI_Aint *size,
    #                          int *disp_unit, void *baseptr)
    API.MPI_Win_shared_query(win, owner_rank, out_len, out_sizeT, out_baseptr)
    out_len[], out_sizeT[], out_baseptr[]
end
function Win_shared_query(::Type{Array{T}}, win::Win, owner_rank::Integer) where T
    len, sizeT, ptr = Win_shared_query(Ptr{T}, win, owner_rank)
    sizeT == sizeof(T) || error("type sizes don't match")
    # the ptr may be invalid for empty arrays, which will cause an error as unsafe_wrap
    # checks the alignment of ptr, even for length 0
    if len > 0
        return unsafe_wrap(Array, ptr, div(len, sizeT))
    else
        return Array{T}(undef, 0)
    end
end
Win_shared_query(::Type{Array{T}}, dims, win::Win, owner_rank::Integer) where T =
    reshape(Win_shared_query(Array{T}, win, owner_rank), dims)


"""
    MPI.Win_create_dynamic(comm::Comm; infokws...)

Create a dynamic window returning a `Win` object used by these processes to perform RMA operations

{'fraction_non_alphanumeric': 0.14364784225547003, 'fraction_numerical': 0.0006053792268442445, 'mean_word_length': 4.522445081184336, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4391257', 'n_tokens_mistral': 4093, 'n_tokens_neox': 4044, 'n_words': 1090}