Blame src/mpi/pt2pt/bsendutil.c

Packit Service c5cf8c
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
Packit Service c5cf8c
/*
Packit Service c5cf8c
 *  (C) 2001 by Argonne National Laboratory.
Packit Service c5cf8c
 *      See COPYRIGHT in top-level directory.
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
#include "mpiimpl.h"
Packit Service c5cf8c
#include "mpii_bsend.h"
Packit Service c5cf8c
#include "bsendutil.h"
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * Miscellaneous comments
Packit Service c5cf8c
 * By storing total_size along with "size available for messages", we
Packit Service c5cf8c
 * avoid any complexities associated with alignment, since we must
Packit Service c5cf8c
 * ensure that each KPIR_Bsend_data_t structure is properly aligned
Packit Service c5cf8c
 * (i.e., we can't simply do (sizeof(MPII_Bsend_data_t) + size) to get
Packit Service c5cf8c
 * total_size).
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * Function Summary
Packit Service c5cf8c
 *   MPIR_Bsend_attach - Performs the work of MPI_Buffer_attach
Packit Service c5cf8c
 *   MPIR_Bsend_detach - Performs the work of MPI_Buffer_detach
Packit Service c5cf8c
 *   MPIR_Bsend_isend  - Essentially performs an MPI_Ibsend.  Returns
Packit Service c5cf8c
 *                an MPIR_Request that is also stored internally in the
Packit Service c5cf8c
 *                corresponding MPII_Bsend_data_t entry
Packit Service c5cf8c
 *   MPIR_Bsend_free_segment - Free a buffer that is no longer needed,
Packit Service c5cf8c
 *                merging with adjacent segments
Packit Service c5cf8c
 *   MPIR_Bsend_check_active - Check for completion of any pending sends
Packit Service c5cf8c
 *                for bsends (all bsends, both MPI_Ibsend and MPI_Bsend,
Packit Service c5cf8c
 *                are internally converted into Isends on the data
Packit Service c5cf8c
 *                in the Bsend buffer)
Packit Service c5cf8c
 *   MPIR_Bsend_retry_pending - Routine for future use to handle the
Packit Service c5cf8c
 *                case where an Isend cannot be initiated.
Packit Service c5cf8c
 *   MPIR_Bsend_find_buffer - Find a buffer in the bsend buffer large
Packit Service c5cf8c
 *                enough for the message.  However, does not acquire that
Packit Service c5cf8c
 *                buffer (see MPIR_Bsend_take_buffer)
Packit Service c5cf8c
 *   MPIR_Bsend_take_buffer - Find and acquire a buffer for a message
Packit Service c5cf8c
 *   MPIR_Bsend_finalize - Finalize handler when Bsend routines are used
Packit Service c5cf8c
 *   MPIR_Bsend_dump - Debugging routine to print the contents of the control
Packit Service c5cf8c
 *                information in the bsend buffer (the MPII_Bsend_data_t entries)
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
#ifdef MPL_USE_DBG_LOGGING
Packit Service c5cf8c
static void MPIR_Bsend_dump(void);
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
#define BSENDDATA_HEADER_TRUE_SIZE (sizeof(MPII_Bsend_data_t) - sizeof(double))
Packit Service c5cf8c
Packit Service c5cf8c
/* BsendBuffer is the structure that describes the overall Bsend buffer */
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * We use separate buffer and origbuffer because we may need to align
Packit Service c5cf8c
 * the buffer (we *could* always memcopy the header to an aligned region,
Packit Service c5cf8c
 * but it is simpler to just align it internally.  This does increase the
Packit Service c5cf8c
 * BSEND_OVERHEAD, but that is already relatively large.  We could instead
Packit Service c5cf8c
 * make sure that the initial header was set at an aligned location (
Packit Service c5cf8c
 * taking advantage of the "alignpad"), but this would require more changes.
Packit Service c5cf8c
 */
Packit Service c5cf8c
static struct BsendBuffer {
Packit Service c5cf8c
    void *buffer;               /* Pointer to the begining of the user-
Packit Service c5cf8c
                                 * provided buffer */
Packit Service c5cf8c
    size_t buffer_size;         /* Size of the user-provided buffer */
Packit Service c5cf8c
    void *origbuffer;           /* Pointer to the buffer provided by
Packit Service c5cf8c
                                 * the user */
Packit Service c5cf8c
    size_t origbuffer_size;     /* Size of the buffer as provided
Packit Service c5cf8c
                                 * by the user */
Packit Service c5cf8c
    MPII_Bsend_data_t *avail;   /* Pointer to the first available block
Packit Service c5cf8c
                                 * of space */
Packit Service c5cf8c
    MPII_Bsend_data_t *pending; /* Pointer to the first message that
Packit Service c5cf8c
                                 * could not be sent because of a
Packit Service c5cf8c
                                 * resource limit (e.g., no requests
Packit Service c5cf8c
                                 * available) */
Packit Service c5cf8c
    MPII_Bsend_data_t *active;  /* Pointer to the first active (sending)
Packit Service c5cf8c
                                 * message */
Packit Service c5cf8c
} BsendBuffer = {
Packit Service c5cf8c
0, 0, 0, 0, 0, 0, 0};
Packit Service c5cf8c
Packit Service c5cf8c
static int initialized = 0;     /* keep track of the first call to any
Packit Service c5cf8c
                                 * bsend routine */
Packit Service c5cf8c
Packit Service c5cf8c
/* Forward references */
Packit Service c5cf8c
static void MPIR_Bsend_retry_pending(void);
Packit Service c5cf8c
static int MPIR_Bsend_check_active(void);
Packit Service c5cf8c
static MPII_Bsend_data_t *MPIR_Bsend_find_buffer(size_t);
Packit Service c5cf8c
static void MPIR_Bsend_take_buffer(MPII_Bsend_data_t *, size_t);
Packit Service c5cf8c
static int MPIR_Bsend_finalize(void *);
Packit Service c5cf8c
static void MPIR_Bsend_free_segment(MPII_Bsend_data_t *);
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * Attach a buffer.  This checks for the error conditions and then
Packit Service c5cf8c
 * initialized the avail buffer.
Packit Service c5cf8c
 */
Packit Service c5cf8c
#undef FUNCNAME
Packit Service c5cf8c
#define FUNCNAME MPIR_Bsend_attach
Packit Service c5cf8c
#undef FCNAME
Packit Service c5cf8c
#define FCNAME MPL_QUOTE(FUNCNAME)
Packit Service c5cf8c
int MPIR_Bsend_attach(void *buffer, int buffer_size)
Packit Service c5cf8c
{
Packit Service c5cf8c
    MPII_Bsend_data_t *p;
Packit Service c5cf8c
    size_t offset, align_sz;
Packit Service c5cf8c
Packit Service c5cf8c
#ifdef HAVE_ERROR_CHECKING
Packit Service c5cf8c
    {
Packit Service c5cf8c
        MPID_BEGIN_ERROR_CHECKS;
Packit Service c5cf8c
        {
Packit Service c5cf8c
            if (BsendBuffer.buffer) {
Packit Service c5cf8c
                return MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE,
Packit Service c5cf8c
                                            "MPIR_Bsend_attach", __LINE__, MPI_ERR_BUFFER,
Packit Service c5cf8c
                                            "**bufexists", 0);
Packit Service c5cf8c
            }
Packit Service c5cf8c
            if (buffer_size < MPI_BSEND_OVERHEAD) {
Packit Service c5cf8c
                /* MPI_ERR_OTHER is another valid choice for this error,
Packit Service c5cf8c
                 * but the Intel test wants MPI_ERR_BUFFER, and it seems
Packit Service c5cf8c
                 * to violate the principle of least surprise to not use
Packit Service c5cf8c
                 * MPI_ERR_BUFFER for errors with the Buffer */
Packit Service c5cf8c
                return MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE,
Packit Service c5cf8c
                                            "MPIR_Bsend_attach", __LINE__, MPI_ERR_BUFFER,
Packit Service c5cf8c
                                            "**bsendbufsmall",
Packit Service c5cf8c
                                            "**bsendbufsmall %d %d", buffer_size,
Packit Service c5cf8c
                                            MPI_BSEND_OVERHEAD);
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
        MPID_END_ERROR_CHECKS;
Packit Service c5cf8c
    }
Packit Service c5cf8c
#endif /* HAVE_ERROR_CHECKING */
Packit Service c5cf8c
Packit Service c5cf8c
    if (!initialized) {
Packit Service c5cf8c
        initialized = 1;
Packit Service c5cf8c
        MPIR_Add_finalize(MPIR_Bsend_finalize, (void *) 0, 10);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    BsendBuffer.origbuffer = buffer;
Packit Service c5cf8c
    BsendBuffer.origbuffer_size = buffer_size;
Packit Service c5cf8c
    BsendBuffer.buffer = buffer;
Packit Service c5cf8c
    BsendBuffer.buffer_size = buffer_size;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Make sure that the buffer that we use is aligned to align_sz.  Some other
Packit Service c5cf8c
     * code assumes pointer-alignment, and some code assumes double alignment.
Packit Service c5cf8c
     * Further, GCC 4.5.1 generates bad code on 32-bit platforms when this is
Packit Service c5cf8c
     * only 4-byte aligned (see #1149). */
Packit Service c5cf8c
    align_sz = MPL_MAX(sizeof(void *), sizeof(double));
Packit Service c5cf8c
    offset = ((size_t) buffer) % align_sz;
Packit Service c5cf8c
    if (offset) {
Packit Service c5cf8c
        offset = align_sz - offset;
Packit Service c5cf8c
        buffer = (char *) buffer + offset;
Packit Service c5cf8c
        BsendBuffer.buffer = buffer;
Packit Service c5cf8c
        BsendBuffer.buffer_size -= offset;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    BsendBuffer.avail = buffer;
Packit Service c5cf8c
    BsendBuffer.pending = 0;
Packit Service c5cf8c
    BsendBuffer.active = 0;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Set the first block */
Packit Service c5cf8c
    p = (MPII_Bsend_data_t *) buffer;
Packit Service c5cf8c
    p->size = buffer_size - BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
    p->total_size = buffer_size;
Packit Service c5cf8c
    p->next = p->prev = NULL;
Packit Service c5cf8c
    p->msg.msgbuf = (char *) p + BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
Packit Service c5cf8c
    return MPI_SUCCESS;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * Detach a buffer.  This routine must wait until any pending bsends
Packit Service c5cf8c
 * are complete.  Note that MPI specifies the type of the returned "size"
Packit Service c5cf8c
 * argument as an "int" (the definition predates that of ssize_t as a
Packit Service c5cf8c
 * standard type).
Packit Service c5cf8c
 */
Packit Service c5cf8c
#undef FUNCNAME
Packit Service c5cf8c
#define FUNCNAME MPIR_Bsend_detach
Packit Service c5cf8c
#undef FCNAME
Packit Service c5cf8c
#define FCNAME MPL_QUOTE(FUNCNAME)
Packit Service c5cf8c
int MPIR_Bsend_detach(void *bufferp, int *size)
Packit Service c5cf8c
{
Packit Service c5cf8c
    int mpi_errno = MPI_SUCCESS;
Packit Service c5cf8c
Packit Service c5cf8c
    if (BsendBuffer.pending) {
Packit Service c5cf8c
        /* FIXME: Process pending bsend requests in detach */
Packit Service c5cf8c
        return MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE,
Packit Service c5cf8c
                                    "MPIR_Bsend_detach", __LINE__, MPI_ERR_OTHER, "**bsendpending",
Packit Service c5cf8c
                                    0);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    if (BsendBuffer.active) {
Packit Service c5cf8c
        /* Loop through each active element and wait on it */
Packit Service c5cf8c
        MPII_Bsend_data_t *p = BsendBuffer.active;
Packit Service c5cf8c
Packit Service c5cf8c
        while (p) {
Packit Service c5cf8c
            MPI_Request r = p->request->handle;
Packit Service c5cf8c
            mpi_errno = MPIR_Wait(&r, MPI_STATUS_IGNORE);
Packit Service c5cf8c
            if (mpi_errno)
Packit Service c5cf8c
                MPIR_ERR_POP(mpi_errno);
Packit Service c5cf8c
            p = p->next;
Packit Service c5cf8c
        }
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
/* Note that this works even when the buffer does not exist */
Packit Service c5cf8c
    *(void **) bufferp = BsendBuffer.origbuffer;
Packit Service c5cf8c
    /* This cast to int will work because the user must use an int to describe
Packit Service c5cf8c
     * the buffer size */
Packit Service c5cf8c
    *size = (int) BsendBuffer.origbuffer_size;
Packit Service c5cf8c
    BsendBuffer.origbuffer = NULL;
Packit Service c5cf8c
    BsendBuffer.origbuffer_size = 0;
Packit Service c5cf8c
    BsendBuffer.buffer = 0;
Packit Service c5cf8c
    BsendBuffer.buffer_size = 0;
Packit Service c5cf8c
    BsendBuffer.avail = 0;
Packit Service c5cf8c
    BsendBuffer.active = 0;
Packit Service c5cf8c
    BsendBuffer.pending = 0;
Packit Service c5cf8c
Packit Service c5cf8c
  fn_exit:
Packit Service c5cf8c
    return mpi_errno;
Packit Service c5cf8c
  fn_fail:
Packit Service c5cf8c
    goto fn_exit;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * Initiate an ibsend.  We'll used this for Bsend as well.
Packit Service c5cf8c
 */
Packit Service c5cf8c
#undef FUNCNAME
Packit Service c5cf8c
#define FUNCNAME MPIR_Bsend_isend
Packit Service c5cf8c
#undef FCNAME
Packit Service c5cf8c
#define FCNAME MPL_QUOTE(FUNCNAME)
Packit Service c5cf8c
int MPIR_Bsend_isend(const void *buf, int count, MPI_Datatype dtype,
Packit Service c5cf8c
                     int dest, int tag, MPIR_Comm * comm_ptr,
Packit Service c5cf8c
                     MPII_Bsend_kind_t kind, MPIR_Request ** request)
Packit Service c5cf8c
{
Packit Service c5cf8c
    int mpi_errno = MPI_SUCCESS;
Packit Service c5cf8c
    MPII_Bsend_data_t *p;
Packit Service c5cf8c
    MPII_Bsend_msg_t *msg;
Packit Service c5cf8c
    MPI_Aint packsize;
Packit Service c5cf8c
    int pass;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Find a free segment and copy the data into it.  If we could
Packit Service c5cf8c
     * have, we would already have used tBsend to send the message with
Packit Service c5cf8c
     * no copying.
Packit Service c5cf8c
     *
Packit Service c5cf8c
     * We may want to decide here whether we need to pack at all
Packit Service c5cf8c
     * or if we can just use (a MPIR_Memcpy) of the buffer.
Packit Service c5cf8c
     */
Packit Service c5cf8c
Packit Service c5cf8c
Packit Service c5cf8c
    /* We check the active buffer first.  This helps avoid storage
Packit Service c5cf8c
     * fragmentation */
Packit Service c5cf8c
    mpi_errno = MPIR_Bsend_check_active();
Packit Service c5cf8c
    if (mpi_errno)
Packit Service c5cf8c
        MPIR_ERR_POP(mpi_errno);
Packit Service c5cf8c
Packit Service c5cf8c
    if (dtype != MPI_PACKED)
Packit Service c5cf8c
        MPIR_Pack_size_impl(count, dtype, &packsize);
Packit Service c5cf8c
    else
Packit Service c5cf8c
        packsize = count;
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_D(MPIR_DBG_BSEND, TYPICAL, "looking for buffer of size " MPI_AINT_FMT_DEC_SPEC,
Packit Service c5cf8c
                  packsize);
Packit Service c5cf8c
    /*
Packit Service c5cf8c
     * Use two passes.  Each pass is the same; between the two passes,
Packit Service c5cf8c
     * attempt to complete any active requests, and start any pending
Packit Service c5cf8c
     * ones.  If the message can be initiated in the first pass,
Packit Service c5cf8c
     * do not perform the second pass.
Packit Service c5cf8c
     */
Packit Service c5cf8c
    for (pass = 0; pass < 2; pass++) {
Packit Service c5cf8c
Packit Service c5cf8c
        p = MPIR_Bsend_find_buffer(packsize);
Packit Service c5cf8c
        if (p) {
Packit Service c5cf8c
            MPL_DBG_MSG_FMT(MPIR_DBG_BSEND, TYPICAL, (MPL_DBG_FDEST,
Packit Service c5cf8c
                                                      "found buffer of size " MPI_AINT_FMT_DEC_SPEC
Packit Service c5cf8c
                                                      " with address %p", packsize, p));
Packit Service c5cf8c
            /* Found a segment */
Packit Service c5cf8c
Packit Service c5cf8c
            msg = &p->msg;
Packit Service c5cf8c
Packit Service c5cf8c
            /* Pack the data into the buffer */
Packit Service c5cf8c
            /* We may want to optimize for the special case of
Packit Service c5cf8c
             * either primative or contiguous types, and just
Packit Service c5cf8c
             * use MPIR_Memcpy and the provided datatype */
Packit Service c5cf8c
            msg->count = 0;
Packit Service c5cf8c
            if (dtype != MPI_PACKED) {
Packit Service c5cf8c
                mpi_errno =
Packit Service c5cf8c
                    MPIR_Pack_impl(buf, count, dtype, p->msg.msgbuf, packsize, &p->msg.count);
Packit Service c5cf8c
                if (mpi_errno)
Packit Service c5cf8c
                    MPIR_ERR_POP(mpi_errno);
Packit Service c5cf8c
            } else {
Packit Service c5cf8c
                MPIR_Memcpy(p->msg.msgbuf, buf, count);
Packit Service c5cf8c
                p->msg.count = count;
Packit Service c5cf8c
            }
Packit Service c5cf8c
            /* Try to send the message.  We must use MPID_Isend
Packit Service c5cf8c
             * because this call must not block */
Packit Service c5cf8c
            mpi_errno = MPID_Isend(msg->msgbuf, msg->count, MPI_PACKED,
Packit Service c5cf8c
                                   dest, tag, comm_ptr, MPIR_CONTEXT_INTRA_PT2PT, &p->request);
Packit Service c5cf8c
            MPIR_ERR_CHKINTERNAL(mpi_errno, mpi_errno, "Bsend internal error: isend returned err");
Packit Service c5cf8c
            /* If the error is "request not available", we should
Packit Service c5cf8c
             * put this on the pending list.  This will depend on
Packit Service c5cf8c
             * how we signal failure to send. */
Packit Service c5cf8c
Packit Service c5cf8c
            if (p->request) {
Packit Service c5cf8c
                MPL_DBG_MSG_FMT(MPIR_DBG_BSEND, TYPICAL,
Packit Service c5cf8c
                                (MPL_DBG_FDEST, "saving request %p in %p", p->request, p));
Packit Service c5cf8c
                /* An optimization is to check to see if the
Packit Service c5cf8c
                 * data has already been sent.  The original code
Packit Service c5cf8c
                 * to do this was commented out and probably did not match
Packit Service c5cf8c
                 * the current request internals */
Packit Service c5cf8c
                MPIR_Bsend_take_buffer(p, p->msg.count);
Packit Service c5cf8c
                p->kind = kind;
Packit Service c5cf8c
                *request = p->request;
Packit Service c5cf8c
            }
Packit Service c5cf8c
            break;
Packit Service c5cf8c
        }
Packit Service c5cf8c
        /* If we found a buffer or we're in the seccond pass, then break.
Packit Service c5cf8c
         * Note that the test on phere is redundant, as the code breaks
Packit Service c5cf8c
         * out of the loop in the test above if a block p is found. */
Packit Service c5cf8c
        if (p || pass == 1)
Packit Service c5cf8c
            break;
Packit Service c5cf8c
        MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "Could not find storage, checking active");
Packit Service c5cf8c
        /* Try to complete some pending bsends */
Packit Service c5cf8c
        MPIR_Bsend_check_active();
Packit Service c5cf8c
        /* Give priority to any pending operations */
Packit Service c5cf8c
        MPIR_Bsend_retry_pending();
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    if (!p) {
Packit Service c5cf8c
        /* Return error for no buffer space found */
Packit Service c5cf8c
        /* Generate a traceback of the allocated space, explaining why
Packit Service c5cf8c
         * packsize could not be found */
Packit Service c5cf8c
        MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "Could not find space; dumping arena");
Packit Service c5cf8c
        MPL_DBG_STMT(MPIR_DBG_BSEND, TYPICAL, MPIR_Bsend_dump());
Packit Service c5cf8c
        MPIR_ERR_SETANDJUMP2(mpi_errno, MPI_ERR_BUFFER, "**bufbsend", "**bufbsend %d %d", packsize,
Packit Service c5cf8c
                             BsendBuffer.buffer_size);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
  fn_exit:
Packit Service c5cf8c
    return mpi_errno;
Packit Service c5cf8c
  fn_fail:
Packit Service c5cf8c
    goto fn_exit;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * The following routine looks up the segment used by request req
Packit Service c5cf8c
 * and frees it. The request is assumed to be completed. This routine
Packit Service c5cf8c
 * is called by only MPIR_Ibsend_free.
Packit Service c5cf8c
 */
Packit Service c5cf8c
#undef FUNCNAME
Packit Service c5cf8c
#define FUNCNAME MPIR_Bsend_free_seg
Packit Service c5cf8c
#undef FCNAME
Packit Service c5cf8c
#define FCNAME MPL_QUOTE(FUNCNAME)
Packit Service c5cf8c
int MPIR_Bsend_free_req_seg(MPIR_Request * req)
Packit Service c5cf8c
{
Packit Service c5cf8c
    int mpi_errno = MPI_ERR_INTERN;
Packit Service c5cf8c
    MPII_Bsend_data_t *active = BsendBuffer.active;
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_P(MPIR_DBG_BSEND, TYPICAL, "Checking active starting at %p", active);
Packit Service c5cf8c
    while (active) {
Packit Service c5cf8c
Packit Service c5cf8c
        if (active->request == req) {
Packit Service c5cf8c
            MPIR_Bsend_free_segment(active);
Packit Service c5cf8c
            mpi_errno = MPI_SUCCESS;
Packit Service c5cf8c
        }
Packit Service c5cf8c
Packit Service c5cf8c
        active = active->next;;
Packit Service c5cf8c
Packit Service c5cf8c
        MPL_DBG_MSG_P(MPIR_DBG_BSEND, TYPICAL, "Next active is %p", active);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    return mpi_errno;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * The following routines are used to manage the allocation of bsend segments
Packit Service c5cf8c
 * in the user buffer.  These routines handle, for example, merging segments
Packit Service c5cf8c
 * when an active segment that is adjacent to a free segment becomes free.
Packit Service c5cf8c
 *
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
/* Add block p to the free list. Merge into adjacent blocks.  Used only
Packit Service c5cf8c
   within the check_active */
Packit Service c5cf8c
Packit Service c5cf8c
#undef FUNCNAME
Packit Service c5cf8c
#define FUNCNAME MPIR_Bsend_free_segment
Packit Service c5cf8c
#undef FCNAME
Packit Service c5cf8c
#define FCNAME MPL_QUOTE(FUNCNAME)
Packit Service c5cf8c
static void MPIR_Bsend_free_segment(MPII_Bsend_data_t * p)
Packit Service c5cf8c
{
Packit Service c5cf8c
    MPII_Bsend_data_t *prev = p->prev, *avail = BsendBuffer.avail, *avail_prev;
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_FMT(MPIR_DBG_BSEND, TYPICAL, (MPL_DBG_FDEST,
Packit Service c5cf8c
                                              "Freeing bsend segment at %p of size %llu, next at %p",
Packit Service c5cf8c
                                              p, (unsigned long long) p->size,
Packit Service c5cf8c
                                              ((char *) p) + p->total_size));
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_D(MPIR_DBG_BSEND, TYPICAL,
Packit Service c5cf8c
                  "At the begining of free_segment with size %llu:",
Packit Service c5cf8c
                  (unsigned long long) p->total_size);
Packit Service c5cf8c
    MPL_DBG_STMT(MPIR_DBG_BSEND, TYPICAL, MPIR_Bsend_dump());
Packit Service c5cf8c
Packit Service c5cf8c
    /* Remove the segment from the active list */
Packit Service c5cf8c
    if (prev) {
Packit Service c5cf8c
        MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "free segment is within active list");
Packit Service c5cf8c
        prev->next = p->next;
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        /* p was at the head of the active list */
Packit Service c5cf8c
        MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "free segment is head of active list");
Packit Service c5cf8c
        BsendBuffer.active = p->next;
Packit Service c5cf8c
        /* The next test sets the prev pointer to null */
Packit Service c5cf8c
    }
Packit Service c5cf8c
    if (p->next) {
Packit Service c5cf8c
        p->next->prev = prev;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_STMT(MPIR_DBG_BSEND, VERBOSE, MPIR_Bsend_dump());
Packit Service c5cf8c
Packit Service c5cf8c
    /* Merge into the avail list */
Packit Service c5cf8c
    /* Find avail_prev, avail, such that p is between them.
Packit Service c5cf8c
     * either may be null if p is at either end of the list */
Packit Service c5cf8c
    avail_prev = 0;
Packit Service c5cf8c
    while (avail) {
Packit Service c5cf8c
        if (avail > p) {
Packit Service c5cf8c
            break;
Packit Service c5cf8c
        }
Packit Service c5cf8c
        avail_prev = avail;
Packit Service c5cf8c
        avail = avail->next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    /* Try to merge p with the next block */
Packit Service c5cf8c
    if (avail) {
Packit Service c5cf8c
        if ((char *) p + p->total_size == (char *) avail) {
Packit Service c5cf8c
            p->total_size += avail->total_size;
Packit Service c5cf8c
            p->size = p->total_size - BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
            p->next = avail->next;
Packit Service c5cf8c
            if (avail->next)
Packit Service c5cf8c
                avail->next->prev = p;
Packit Service c5cf8c
            avail = 0;
Packit Service c5cf8c
        } else {
Packit Service c5cf8c
            p->next = avail;
Packit Service c5cf8c
            avail->prev = p;
Packit Service c5cf8c
        }
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        p->next = 0;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    /* Try to merge p with the previous block */
Packit Service c5cf8c
    if (avail_prev) {
Packit Service c5cf8c
        if ((char *) avail_prev + avail_prev->total_size == (char *) p) {
Packit Service c5cf8c
            avail_prev->total_size += p->total_size;
Packit Service c5cf8c
            avail_prev->size = avail_prev->total_size - BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
            avail_prev->next = p->next;
Packit Service c5cf8c
            if (p->next)
Packit Service c5cf8c
                p->next->prev = avail_prev;
Packit Service c5cf8c
        } else {
Packit Service c5cf8c
            avail_prev->next = p;
Packit Service c5cf8c
            p->prev = avail_prev;
Packit Service c5cf8c
        }
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        /* p is the new head of the list */
Packit Service c5cf8c
        BsendBuffer.avail = p;
Packit Service c5cf8c
        p->prev = 0;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "At the end of free_segment:");
Packit Service c5cf8c
    MPL_DBG_STMT(MPIR_DBG_BSEND, TYPICAL, MPIR_Bsend_dump());
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * The following routine tests for completion of active sends and
Packit Service c5cf8c
 * frees the related storage
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * To make it easier to identify the source of the request, we keep
Packit Service c5cf8c
 * track of the type of MPI routine (ibsend, bsend, or bsend_init/start)
Packit Service c5cf8c
 * that created the bsend entry.
Packit Service c5cf8c
 */
Packit Service c5cf8c
#undef FUNCNAME
Packit Service c5cf8c
#define FUNCNAME MPIR_Bsend_check_active
Packit Service c5cf8c
#undef FCNAME
Packit Service c5cf8c
#define FCNAME MPL_QUOTE(FUNCNAME)
Packit Service c5cf8c
static int MPIR_Bsend_check_active(void)
Packit Service c5cf8c
{
Packit Service c5cf8c
    int mpi_errno = MPI_SUCCESS;
Packit Service c5cf8c
    MPII_Bsend_data_t *active = BsendBuffer.active, *next_active;
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_P(MPIR_DBG_BSEND, TYPICAL, "Checking active starting at %p", active);
Packit Service c5cf8c
    while (active) {
Packit Service c5cf8c
        MPI_Request r = active->request->handle;
Packit Service c5cf8c
        int flag;
Packit Service c5cf8c
Packit Service c5cf8c
        next_active = active->next;
Packit Service c5cf8c
Packit Service c5cf8c
        if (active->kind == IBSEND) {
Packit Service c5cf8c
            /* We handle ibsend specially to allow for the user
Packit Service c5cf8c
             * to attempt and cancel the request. Also, to allow
Packit Service c5cf8c
             * for a cancel attempt (which must be attempted before
Packit Service c5cf8c
             * a successful test or wait), we only start
Packit Service c5cf8c
             * testing when the user has successfully released
Packit Service c5cf8c
             * the request (it is a grequest, the free call will do it) */
Packit Service c5cf8c
            flag = 0;
Packit Service c5cf8c
            /* XXX DJG FIXME-MT should we be checking this? */
Packit Service c5cf8c
            if (MPIR_Object_get_ref(active->request) == 1) {
Packit Service c5cf8c
                mpi_errno = MPIR_Test(&r, &flag, MPI_STATUS_IGNORE);
Packit Service c5cf8c
                if (mpi_errno)
Packit Service c5cf8c
                    MPIR_ERR_POP(mpi_errno);
Packit Service c5cf8c
            } else {
Packit Service c5cf8c
                /* We need to invoke the progress engine in case we
Packit Service c5cf8c
                 * need to advance other, incomplete communication.  */
Packit Service c5cf8c
                MPID_Progress_state progress_state;
Packit Service c5cf8c
                MPID_Progress_start(&progress_state);
Packit Service c5cf8c
                mpi_errno = MPID_Progress_test();
Packit Service c5cf8c
                MPID_Progress_end(&progress_state);
Packit Service c5cf8c
                if (mpi_errno)
Packit Service c5cf8c
                    MPIR_ERR_POP(mpi_errno);
Packit Service c5cf8c
            }
Packit Service c5cf8c
        } else {
Packit Service c5cf8c
            mpi_errno = MPIR_Test(&r, &flag, MPI_STATUS_IGNORE);
Packit Service c5cf8c
            if (mpi_errno)
Packit Service c5cf8c
                MPIR_ERR_POP(mpi_errno);
Packit Service c5cf8c
        }
Packit Service c5cf8c
        if (flag) {
Packit Service c5cf8c
            /* We're done.  Remove this segment */
Packit Service c5cf8c
            MPL_DBG_MSG_P(MPIR_DBG_BSEND, TYPICAL, "Removing segment %p", active);
Packit Service c5cf8c
            MPIR_Bsend_free_segment(active);
Packit Service c5cf8c
        }
Packit Service c5cf8c
        active = next_active;
Packit Service c5cf8c
        MPL_DBG_MSG_P(MPIR_DBG_BSEND, TYPICAL, "Next active is %p", active);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
  fn_exit:
Packit Service c5cf8c
    return mpi_errno;
Packit Service c5cf8c
  fn_fail:
Packit Service c5cf8c
    goto fn_exit;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * FIXME : For each pending item (that is, items that we couldn't even start
Packit Service c5cf8c
 * sending), try to get them going.
Packit Service c5cf8c
 */
Packit Service c5cf8c
static void MPIR_Bsend_retry_pending(void)
Packit Service c5cf8c
{
Packit Service c5cf8c
    MPII_Bsend_data_t *pending = BsendBuffer.pending, *next_pending;
Packit Service c5cf8c
Packit Service c5cf8c
    while (pending) {
Packit Service c5cf8c
        next_pending = pending->next;
Packit Service c5cf8c
        /* Retry sending this item */
Packit Service c5cf8c
        /* FIXME: Unimplemented retry of pending bsend operations */
Packit Service c5cf8c
        pending = next_pending;
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * Find a slot in the avail buffer that can hold size bytes.  Does *not*
Packit Service c5cf8c
 * remove the slot from the avail buffer (see MPIR_Bsend_take_buffer)
Packit Service c5cf8c
 */
Packit Service c5cf8c
static MPII_Bsend_data_t *MPIR_Bsend_find_buffer(size_t size)
Packit Service c5cf8c
{
Packit Service c5cf8c
    MPII_Bsend_data_t *p = BsendBuffer.avail;
Packit Service c5cf8c
Packit Service c5cf8c
    while (p) {
Packit Service c5cf8c
        if (p->size >= size) {
Packit Service c5cf8c
            return p;
Packit Service c5cf8c
        }
Packit Service c5cf8c
        p = p->next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/* This is the minimum number of bytes that a segment must be able to
Packit Service c5cf8c
   hold. */
Packit Service c5cf8c
#define MIN_BUFFER_BLOCK 8
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * Carve off size bytes from buffer p and leave the remainder
Packit Service c5cf8c
 * on the avail list.  Handle the head/tail cases.
Packit Service c5cf8c
 * If there isn't enough left of p, remove the entire segment from
Packit Service c5cf8c
 * the avail list.
Packit Service c5cf8c
 */
Packit Service c5cf8c
static void MPIR_Bsend_take_buffer(MPII_Bsend_data_t * p, size_t size)
Packit Service c5cf8c
{
Packit Service c5cf8c
    MPII_Bsend_data_t *prev;
Packit Service c5cf8c
    size_t alloc_size;
Packit Service c5cf8c
Packit Service c5cf8c
    /* Compute the remaining size.  This must include any padding
Packit Service c5cf8c
     * that must be added to make the new block properly aligned */
Packit Service c5cf8c
    alloc_size = size;
Packit Service c5cf8c
    if (alloc_size & 0x7)
Packit Service c5cf8c
        alloc_size += (8 - (alloc_size & 0x7));
Packit Service c5cf8c
    /* alloc_size is the amount of space (out of size) that we will
Packit Service c5cf8c
     * allocate for this buffer. */
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_FMT(MPIR_DBG_BSEND, TYPICAL, (MPL_DBG_FDEST,
Packit Service c5cf8c
                                              "Taking %lu bytes from a block with %llu bytes\n",
Packit Service c5cf8c
                                              alloc_size, (unsigned long long) p->total_size));
Packit Service c5cf8c
Packit Service c5cf8c
    /* Is there enough space left to create a new block? */
Packit Service c5cf8c
    if (alloc_size + BSENDDATA_HEADER_TRUE_SIZE + MIN_BUFFER_BLOCK <= p->size) {
Packit Service c5cf8c
        /* Yes, the available space (p->size) is large enough to
Packit Service c5cf8c
         * carve out a new block */
Packit Service c5cf8c
        MPII_Bsend_data_t *newp;
Packit Service c5cf8c
Packit Service c5cf8c
        MPL_DBG_MSG_P(MPIR_DBG_BSEND, TYPICAL, "Breaking block into used and allocated at %p", p);
Packit Service c5cf8c
        newp = (MPII_Bsend_data_t *) ((char *) p + BSENDDATA_HEADER_TRUE_SIZE + alloc_size);
Packit Service c5cf8c
        newp->total_size = p->total_size - alloc_size - BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
        newp->size = newp->total_size - BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
        newp->msg.msgbuf = (char *) newp + BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
Packit Service c5cf8c
        /* Insert this new block after p (we'll remove p from the avail list
Packit Service c5cf8c
         * next) */
Packit Service c5cf8c
        newp->next = p->next;
Packit Service c5cf8c
        newp->prev = p;
Packit Service c5cf8c
        if (p->next) {
Packit Service c5cf8c
            p->next->prev = newp;
Packit Service c5cf8c
        }
Packit Service c5cf8c
        p->next = newp;
Packit Service c5cf8c
        p->total_size = (char *) newp - (char *) p;
Packit Service c5cf8c
        p->size = p->total_size - BSENDDATA_HEADER_TRUE_SIZE;
Packit Service c5cf8c
Packit Service c5cf8c
        MPL_DBG_MSG_FMT(MPIR_DBG_BSEND, TYPICAL, (MPL_DBG_FDEST,
Packit Service c5cf8c
                                                  "broken blocks p (%llu) and new (%llu)\n",
Packit Service c5cf8c
                                                  (unsigned long long) p->total_size,
Packit Service c5cf8c
                                                  (unsigned long long) newp->total_size));
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    /* Remove p from the avail list and add it to the active list */
Packit Service c5cf8c
    prev = p->prev;
Packit Service c5cf8c
    if (prev) {
Packit Service c5cf8c
        prev->next = p->next;
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        BsendBuffer.avail = p->next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    if (p->next) {
Packit Service c5cf8c
        p->next->prev = p->prev;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    if (BsendBuffer.active) {
Packit Service c5cf8c
        BsendBuffer.active->prev = p;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    p->next = BsendBuffer.active;
Packit Service c5cf8c
    p->prev = 0;
Packit Service c5cf8c
    BsendBuffer.active = p;
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_P(MPIR_DBG_BSEND, VERBOSE, "segment %p now head of active", p);
Packit Service c5cf8c
    MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "At end of take buffer");
Packit Service c5cf8c
    MPL_DBG_STMT(MPIR_DBG_BSEND, TYPICAL, MPIR_Bsend_dump());
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static int MPIR_Bsend_finalize(void *p ATTRIBUTE((unused)))
Packit Service c5cf8c
{
Packit Service c5cf8c
    void *b;
Packit Service c5cf8c
    int s;
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_UNREFERENCED_ARG(p);
Packit Service c5cf8c
Packit Service c5cf8c
    if (BsendBuffer.buffer) {
Packit Service c5cf8c
        /* Use detach to complete any communication */
Packit Service c5cf8c
        MPIR_Bsend_detach(&b, &s);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * These routines are defined only if debug logging is enabled
Packit Service c5cf8c
 */
Packit Service c5cf8c
#ifdef MPL_USE_DBG_LOGGING
Packit Service c5cf8c
static void MPIR_Bsend_dump(void)
Packit Service c5cf8c
{
Packit Service c5cf8c
    MPII_Bsend_data_t *a = BsendBuffer.avail;
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG_D(MPIR_DBG_BSEND, TYPICAL, "Total size is %llu",
Packit Service c5cf8c
                  (unsigned long long) BsendBuffer.buffer_size);
Packit Service c5cf8c
    MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "Avail list is:");
Packit Service c5cf8c
    while (a) {
Packit Service c5cf8c
        MPL_DBG_MSG_FMT(MPIR_DBG_BSEND, TYPICAL, (MPL_DBG_FDEST, "[%p] totalsize = %llu(%llx)",
Packit Service c5cf8c
                                                  a, (unsigned long long) a->total_size,
Packit Service c5cf8c
                                                  (unsigned long long) a->total_size));
Packit Service c5cf8c
        if (a == a->next) {
Packit Service c5cf8c
            MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "@@@Corrupt list; avail block points at itself");
Packit Service c5cf8c
            break;
Packit Service c5cf8c
        }
Packit Service c5cf8c
        a = a->next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "Active list is:");
Packit Service c5cf8c
    a = BsendBuffer.active;
Packit Service c5cf8c
    while (a) {
Packit Service c5cf8c
        MPL_DBG_MSG_FMT(MPIR_DBG_BSEND, TYPICAL, (MPL_DBG_FDEST, "[%p] totalsize = %llu(%llx)",
Packit Service c5cf8c
                                                  a, (unsigned long long) a->total_size,
Packit Service c5cf8c
                                                  (unsigned long long) a->total_size));
Packit Service c5cf8c
        if (a == a->next) {
Packit Service c5cf8c
            MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "@@@Corrupt list; active block points at itself");
Packit Service c5cf8c
            break;
Packit Service c5cf8c
        }
Packit Service c5cf8c
        a = a->next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    MPL_DBG_MSG(MPIR_DBG_BSEND, TYPICAL, "end of list");
Packit Service c5cf8c
}
Packit Service c5cf8c
#endif