# vim: set ft=c:

MPI_Cancel:
    .desc: Cancels a communication request
    .docnotes: NULL

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

MPI_Request_get_status:
    .desc: Nondestructive test for the completion of a Request
{ -- 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

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
{
    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
{
    /* 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
{ -- 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
{
    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
{
    /* 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
    .docnotes: 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;
    }
}
