# vim: set ft=c:

MPI_Cancel:
    .desc: Cancels a communication request
    .docnotes: NULL
/*
    Notes:
    The primary expected use of 'MPI_Cancel' is in multi-buffering
    schemes, where speculative 'MPI_Irecvs' are made.  When the computation
    completes, some of these receive requests may remain; using 'MPI_Cancel' allows
    the user to cancel these unsatisfied requests.

    Cancelling a send operation is much more difficult, in large part because the
    send will usually be at least partially complete (the information on the tag,
    size, and source are usually sent immediately to the destination).
    Users are
    advised that cancelling a send, while a local operation (as defined by the MPI
    standard), is likely to be expensive (usually generating one or more internal
    messages).
*/

MPI_Grequest_complete:
    .desc: Notify MPI that a user-defined request is complete
    .seealso: MPI_Grequest_start
{ -- error_check --
    if (request_ptr && request_ptr->kind != MPIR_REQUEST_KIND__GREQUEST) {
        mpi_errno = MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, __func__, __LINE__,
                                         MPI_ERR_REQUEST, "**notgenreq", 0);
        goto fn_fail;
    }
}

MPI_Grequest_start:
    .desc: Create and return a user-defined request

MPIX_Grequest_start:
    query_fn: FUNCTION_SMALL, func_type=MPI_Grequest_query_function, [callback function invoked when request status is queried]
    free_fn: FUNCTION_SMALL, func_type=MPI_Grequest_free_function, [callback function invoked when request is freed]
    cancel_fn: FUNCTION_SMALL, func_type=MPI_Grequest_cancel_function, [callback function invoked when request is cancelled]
    poll_fn: FUNCTION_SMALL, func_type=MPIX_Grequest_poll_function, [callback function invoked when request completion is tested]
    wait_fn: FUNCTION_SMALL, func_type=MPIX_Grequest_wait_function, [callback function invoked when request is waited on]
    extra_state: EXTRA_STATE, [extra state]
    request: REQUEST, direction=out, [generalized request]
    .file: grequest_start_x
    .desc: Create and return a user-defined extended request
{
    MPIR_Request *request_ptr = NULL;
    mpi_errno = MPIR_Grequest_start_impl(query_fn, free_fn, cancel_fn, extra_state, &request_ptr);
    if (mpi_errno) {
        goto fn_fail;
    }
    request_ptr->u.ureq.greq_fns->poll_fn = poll_fn;
    request_ptr->u.ureq.greq_fns->wait_fn = wait_fn;
    *request = request_ptr->handle;
}

MPIX_Grequest_class_create:
    query_fn: FUNCTION_SMALL, func_type=MPI_Grequest_query_function, [callback function invoked when request status is queried]
    free_fn: FUNCTION_SMALL, func_type=MPI_Grequest_free_function, [callback function invoked when request is freed]
    cancel_fn: FUNCTION_SMALL, func_type=MPI_Grequest_cancel_function, [callback function invoked when request is cancelled]
    poll_fn: FUNCTION_SMALL, func_type=MPIX_Grequest_poll_function, [callback function invoked when request completion is tested]
    wait_fn: FUNCTION_SMALL, func_type=MPIX_Grequest_wait_function, [callback function invoked when request is waited on]
    greq_class: GREQUEST_CLASS, direction=out, [generalized request class]

MPIX_Grequest_class_allocate:
    greq_class: GREQUEST_CLASS, [generalized request class]
    extra_state: EXTRA_STATE, [extra state]
    request: REQUEST, direction=out, [generalized request]
    .desc: Create and return a user-defined extended request based on a generalized request class

MPI_Request_free:
    .desc: Frees a communication request object
    .seealso: MPI_Isend, MPI_Irecv, MPI_Issend, MPI_Ibsend, MPI_Irsend, MPI_Recv_init, MPI_Send_init, MPI_Ssend_init, MPI_Rsend_init, MPI_Wait, MPI_Test, MPI_Waitall, MPI_Waitany, MPI_Waitsome, MPI_Testall, MPI_Testany, MPI_Testsome
/*
    Notes:

    This routine is normally used to free inactive persistent requests created with
    either 'MPI_Recv_init' or 'MPI_Send_init' and friends.  It `is` also
    permissible to free an active request.  However, once freed, the request can no
    longer be used in a wait or test routine (e.g., 'MPI_Wait') to determine
    completion.

    This routine may also be used to free a non-persistent requests such as those
    created with 'MPI_Irecv' or 'MPI_Isend' and friends.  Like active persistent
    requests, once freed, the request can no longer be used with test/wait routines
    to determine completion.
*/

MPI_Request_get_status:
    .desc: Nondestructive test for the completion of a Request
/*
    Notes:
    Unlike 'MPI_Test', 'MPI_Request_get_status' does not deallocate or
    deactivate the request.  A call to one of the test/wait routines or
    'MPI_Request_free' should be made to release the request object.
*/
{ -- early_return --
    if (request == MPI_REQUEST_NULL || !MPIR_Request_is_active(request_ptr)) {
        *flag = 1;
        MPIR_Status_set_empty(status);
        goto fn_exit;
    }
}
{
    mpi_errno = MPIR_Test(request_ptr, flag, status);
    if (mpi_errno) {
        goto fn_fail;
    }
}

MPI_Request_get_status_all:
    .desc: Nondestructive test for the completion of all requests
{
    mpi_errno = MPIR_Testall(count, request_ptrs, flag, array_of_statuses);
    if (mpi_errno) {
        goto fn_fail;
    }
}

MPI_Request_get_status_any:
    .desc: Nondestructive test for the completion of any of the requests
{
    mpi_errno = MPIR_Testany(count, request_ptrs, indx, flag, status);
    if (mpi_errno) {
        goto fn_fail;
    }
}

MPI_Request_get_status_some:
    .desc: Nondestructive test for the completion of some of the requests
{ -- error_check -- array_of_indices
    if (incount > 0) {
        MPIR_ERRTEST_ARGNULL(array_of_requests, "array_of_requests", mpi_errno);
        MPIR_ERRTEST_ARGNULL(array_of_indices, "array_of_indices", mpi_errno);
    }
}
{
    mpi_errno = MPIR_Testsome(incount, request_ptrs, outcount,
                              array_of_indices, array_of_statuses);
    if (mpi_errno) {
        goto fn_fail;
    }
}

MPI_Start:
    .desc: Initiates a communication with a persistent request handle
{
    mpi_errno = MPID_Startall(1, &request_ptr);
    if (mpi_errno != MPI_SUCCESS)
        goto fn_fail;
}

MPI_Startall:
    .desc: Starts a collection of persistent requests
    .impl: mpid
/*
    Notes:
    Unlike 'MPI_Waitall', 'MPI_Startall' does not provide a mechanism for
    returning multiple errors nor pinpointing the request(s) involved.
    Furthermore, the behavior of 'MPI_Startall' after an error occurs is not
    defined by the MPI standard.  If well-defined error reporting and behavior
    are required, multiple calls to 'MPI_Start' should be used instead.
*/

MPI_Status_get_error:
    .desc: Get MPI_ERROR field from the status
    .skip: initcheck, global_cs
    .impl: direct
{
    *error = status->MPI_ERROR;
    return MPI_SUCCESS;
}

MPI_Status_get_source:
    .desc: Get MPI_SOURCE field from the status
    .skip: initcheck, global_cs
    .impl: direct
{
    *source = status->MPI_SOURCE;
    return MPI_SUCCESS;
}

MPI_Status_get_tag:
    .desc: Get MPI_TAG field from the status
    .skip: initcheck, global_cs
    .impl: direct
{
    *tag = status->MPI_TAG;
    return MPI_SUCCESS;
}

MPI_Status_set_error:
    .desc: Set MPI_ERROR field in the status
    .skip: initcheck, global_cs, validate-error
    .impl: direct
{
    status->MPI_ERROR = error;
    return MPI_SUCCESS;
}

MPI_Status_set_source:
    .desc: Set MPI_SOURCE field in the status
    .skip: initcheck, global_cs, validate-source
    .impl: direct
{
    status->MPI_SOURCE = source;
    return MPI_SUCCESS;
}

MPI_Status_set_tag:
    .desc: Set MPI_TAG field in the status
    .skip: initcheck, global_cs, validate-tag
    .impl: direct
{
    status->MPI_TAG = tag;
    return MPI_SUCCESS;
}


MPI_Status_set_cancelled:
    .desc: Sets the cancelled state associated with a request
    .skip: initcheck, global_cs

MPI_Test_cancelled:
    .desc: Tests to see if a request was cancelled
    .skip: global_cs

MPI_Test:
    .desc: Test for the completion of a request
    .docnotes: waitstatus
{ -- early_return --
    if (*request == MPI_REQUEST_NULL || !MPIR_Request_is_active(request_ptr)) {
        *flag = 1;
        MPIR_Status_set_empty(status);
        goto fn_exit;
    }
}
{
    mpi_errno = MPIR_Test(request_ptr, flag, status);
    if (*flag) {
        if (!MPIR_Request_is_persistent(request_ptr)) {
            int rc = MPIR_Request_free_return(request_ptr);
            *request = MPI_REQUEST_NULL;
            if (rc) {
                mpi_errno = rc;
            }
        }
    }
    if (mpi_errno)
        goto fn_fail;
}

MPI_Testall:
    .desc: Tests for the completion of all previously initiated requests
    .docnotes: waitstatus
    .error: MPI_ERR_IN_STATUS
/*
    Notes:
      'flag' is true only if all requests have completed.  Otherwise, flag is
      false and neither the 'array_of_requests' nor the 'array_of_statuses' is
      modified.

    If one or more of the requests completes with an error, 'MPI_ERR_IN_STATUS' is
    returned.  An error value will be present is elements of 'array_of_statuses'
    associated with the requests.  Likewise, the 'MPI_ERROR' field in the status
    elements associated with requests that have successfully completed will be
    'MPI_SUCCESS'.  Finally, those requests that have not completed will have a
    value of 'MPI_ERR_PENDING'.

    While it is possible to list a request handle more than once in the
    'array_of_requests', such an action is considered erroneous and may cause the
    program to unexecpectedly terminate or produce incorrect results.
*/
{
    mpi_errno = MPIR_Testall(count, request_ptrs, flag, array_of_statuses);
    if (*flag) {
        for (int i = 0; i < count; i++) {
            if (mpi_errno && array_of_statuses != MPI_STATUSES_IGNORE
                && array_of_statuses[i].MPI_ERROR == MPI_ERR_PENDING) {
                continue;
            }
            if (request_ptrs[i] && !MPIR_Request_is_persistent(request_ptrs[i])) {
                int rc = MPIR_Request_free_return(request_ptrs[i]);
                array_of_requests[i] = MPI_REQUEST_NULL;
                if (rc) {
                    if (array_of_statuses == MPI_STATUSES_IGNORE) {
                        mpi_errno = MPI_ERR_IN_STATUS;
                    } else if (mpi_errno == MPI_ERR_IN_STATUS) {
                        array_of_statuses[i].MPI_ERROR = rc;
                    } else {
                        MPIR_Assert(mpi_errno == MPI_SUCCESS);
                        /* init all statuses' MPI_ERROR fields first */
                        for (int j = 0; j < count; j++) {
                            array_of_statuses[j].MPI_ERROR = MPI_SUCCESS;
                        }
                        mpi_errno = MPI_ERR_IN_STATUS;
                        array_of_statuses[i].MPI_ERROR = rc;
                    }
                }
            }
        }
    }
    if (mpi_errno != MPI_SUCCESS)
        goto fn_fail;
}

MPI_Testany:
    .desc: Tests for completion of any previdously initiated requests
    .docnotes: waitstatus
/*
    Notes:

    While it is possible to list a request handle more than once in the
    'array_of_requests', such an action is considered erroneous and may cause the
    program to unexecpectedly terminate or produce incorrect results.
*/
{
    /* Pass down request ptr to avoid redundant handle to ptr translation. */
    mpi_errno = MPIR_Testany(count, request_ptrs, indx, flag, status);
    if (*indx != MPI_UNDEFINED) {
        if (request_ptrs[*indx] && !MPIR_Request_is_persistent(request_ptrs[*indx])) {
            int rc = MPIR_Request_free_return(request_ptrs[*indx]);
            array_of_requests[*indx] = MPI_REQUEST_NULL;
            if (rc) {
                mpi_errno = rc;
            }
        }
    }
    if (mpi_errno != MPI_SUCCESS)
        goto fn_fail;
}

MPI_Testsome:
    .desc: Tests for some given requests to complete
    .docnotes: waitstatus
/*
    Notes:

    While it is possible to list a request handle more than once in the
    'array_of_requests', such an action is considered erroneous and may cause the
    program to unexecpectedly terminate or produce incorrect results.
*/
{ -- error_check -- array_of_indices
    if (incount > 0) {
        MPIR_ERRTEST_ARGNULL(array_of_requests, "array_of_requests", mpi_errno);
        MPIR_ERRTEST_ARGNULL(array_of_indices, "array_of_indices", mpi_errno);
    }
}
{
    /* Pass down request ptr to avoid redundant handle to ptr translation. */
    mpi_errno = MPIR_Testsome(incount, request_ptrs, outcount,
                              array_of_indices, array_of_statuses);
    for (int i = 0; i < *outcount; i++) {
        int idx = array_of_indices[i];
        if (request_ptrs[idx] && !MPIR_Request_is_persistent(request_ptrs[idx])) {
            int rc = MPIR_Request_free_return(request_ptrs[idx]);
            array_of_requests[idx] = MPI_REQUEST_NULL;
            if (rc) {
                if (array_of_statuses == MPI_STATUSES_IGNORE) {
                    mpi_errno = MPI_ERR_IN_STATUS;
                } else if (mpi_errno == MPI_ERR_IN_STATUS) {
                    array_of_statuses[i].MPI_ERROR = rc;
                } else {
                    MPIR_Assert(mpi_errno == MPI_SUCCESS);
                    /* init all statuses' MPI_ERROR fields first */
                    for (int j = 0; j < *outcount; j++) {
                        array_of_statuses[j].MPI_ERROR = MPI_SUCCESS;
                    }
                    mpi_errno = MPI_ERR_IN_STATUS;
                    array_of_statuses[i].MPI_ERROR = rc;
                }
            }
        }
    }
    if (mpi_errno != MPI_SUCCESS)
        goto fn_fail;
}

MPI_Wait:
    .desc: Waits for an MPI request to complete
    .docnotes: waitstatus
{ -- early_return --
    if (*request == MPI_REQUEST_NULL || !MPIR_Request_is_active(request_ptr)) {
        MPIR_Status_set_empty(status);
        goto fn_exit;
    }
}
{
    mpi_errno = MPIR_Wait(request_ptr, status);
    if (!MPIR_Request_is_persistent(request_ptr)) {
        int rc = MPIR_Request_free_return(request_ptr);
        *request = MPI_REQUEST_NULL;
        if (rc) {
            mpi_errno = rc;
        }
    }
    if (mpi_errno)
        goto fn_fail;

}

MPI_Waitall:
    .desc: Waits for all given MPI Requests to complete
    .docnotes: waitstatus
    .error: MPI_ERR_IN_STATUS
/*
    Notes:

    If one or more of the requests completes with an error, 'MPI_ERR_IN_STATUS' is
    returned.  An error value will be present is elements of 'array_of_statuses'
    associated with the requests.  Likewise, the 'MPI_ERROR' field in the status
    elements associated with requests that have successfully completed will be
    'MPI_SUCCESS'.  Finally, those requests that have not completed will have a
    value of 'MPI_ERR_PENDING'.

    While it is possible to list a request handle more than once in the
    array_of_requests, such an action is considered erroneous and may cause the
    program to unexecpectedly terminate or produce incorrect results.
*/
{
    mpi_errno = MPIR_Waitall(count, request_ptrs, array_of_statuses);
    for (int i = 0; i < count; i++) {
        if (mpi_errno && array_of_statuses != MPI_STATUSES_IGNORE
            && array_of_statuses[i].MPI_ERROR == MPI_ERR_PENDING) {
            continue;
        }
        if (request_ptrs[i] && !MPIR_Request_is_persistent(request_ptrs[i])) {
            int rc = MPIR_Request_free_return(request_ptrs[i]);
            array_of_requests[i] = MPI_REQUEST_NULL;
            if (rc) {
                if (array_of_statuses == MPI_STATUSES_IGNORE) {
                    mpi_errno = MPI_ERR_IN_STATUS;
                } else if (mpi_errno == MPI_ERR_IN_STATUS) {
                    array_of_statuses[i].MPI_ERROR = rc;
                } else {
                    MPIR_Assert(mpi_errno == MPI_SUCCESS);
                    /* init all statuses' MPI_ERROR fields first */
                    for (int j = 0; j < count; j++) {
                        array_of_statuses[j].MPI_ERROR = MPI_SUCCESS;
                    }
                    mpi_errno = MPI_ERR_IN_STATUS;
                    array_of_statuses[i].MPI_ERROR = rc;
                }
            }
        }
    }
    if (mpi_errno)
        goto fn_fail;
}

MPI_Waitany:
    .desc: Waits for any specified MPI Request to complete
    .docnotes: waitstatus
/*
    Notes:
    If all of the requests are 'MPI_REQUEST_NULL', then 'indx' is returned as
    'MPI_UNDEFINED', and 'status' is returned as an empty status.

    While it is possible to list a request handle more than once in the
    array_of_requests, such an action is considered erroneous and may cause the
    program to unexecpectedly terminate or produce incorrect results.
*/
{
    /* Pass down request ptr to avoid redundant handle to ptr translation. */
    mpi_errno = MPIR_Waitany(count, request_ptrs, indx, status);
    if (*indx != MPI_UNDEFINED) {
        if (request_ptrs[*indx] && !MPIR_Request_is_persistent(request_ptrs[*indx])) {
            int rc = MPIR_Request_free_return(request_ptrs[*indx]);
            array_of_requests[*indx] = MPI_REQUEST_NULL;
            if (rc) {
                mpi_errno = rc;
            }
        }
    }
    if (mpi_errno)
        goto fn_fail;
}

MPI_Waitsome:
    .desc: Waits for some given MPI Requests to complete
/*
    Notes:
    The array of indices are in the range '0' to 'incount - 1' for C and
    in the range '1' to 'incount' for Fortran.

    Null requests are ignored; if all requests are null, then the routine
    returns with 'outcount' set to 'MPI_UNDEFINED'.

    While it is possible to list a request handle more than once in the
    array_of_requests, such an action is considered erroneous and may cause the
    program to unexecpectedly terminate or produce incorrect results.

    'MPI_Waitsome' provides an interface much like the Unix 'select' or 'poll'
    calls and, in a high qualilty implementation, indicates all of the requests
    that have completed when 'MPI_Waitsome' is called.
    However, 'MPI_Waitsome' only guarantees that at least one
    request has completed; there is no guarantee that `all` completed requests
    will be returned, or that the entries in 'array_of_indices' will be in
    increasing order. Also, requests that are completed while 'MPI_Waitsome' is
    executing may or may not be returned, depending on the timing of the
    completion of the message.

    .N waitstatus
*/
{ -- error_check -- array_of_indices
    if (incount > 0) {
        MPIR_ERRTEST_ARGNULL(array_of_indices, "array_of_indices", mpi_errno);
    }
}
{
    /* Pass down request ptr to avoid redundant handle to ptr translation. */
    mpi_errno = MPIR_Waitsome(incount, request_ptrs, outcount,
                              array_of_indices, array_of_statuses);
    for (int i = 0; i < *outcount; i++) {
        int idx = array_of_indices[i];
        if (request_ptrs[idx] && !MPIR_Request_is_persistent(request_ptrs[idx])) {
            int rc = MPIR_Request_free_return(request_ptrs[idx]);
            array_of_requests[idx] = MPI_REQUEST_NULL;
            if (rc) {
                if (array_of_statuses == MPI_STATUSES_IGNORE) {
                    mpi_errno = MPI_ERR_IN_STATUS;
                } else if (mpi_errno == MPI_ERR_IN_STATUS) {
                    array_of_statuses[i].MPI_ERROR = rc;
                } else {
                    MPIR_Assert(mpi_errno == MPI_SUCCESS);
                    /* init all statuses' MPI_ERROR fields first */
                    for (int j = 0; j < *outcount; j++) {
                        array_of_statuses[j].MPI_ERROR = MPI_SUCCESS;
                    }
                    mpi_errno = MPI_ERR_IN_STATUS;
                    array_of_statuses[i].MPI_ERROR = rc;
                }
            }
        }
    }
    if (mpi_errno)
        goto fn_fail;
}

MPIX_Request_is_complete:
    .return: LOGICAL
    request: REQUEST, direction=in, pointer=False
    .impl: direct
{
    MPIR_Request *request_ptr;
    MPIR_Request_get_ptr(request, request_ptr);
    if (request_ptr) {
        return MPIR_Request_is_complete(request_ptr);
    } else {
        return 1;
    }
}
