/****************************************************************************
**  SCALASCA    http://www.scalasca.org/                                   **
*****************************************************************************
**  Copyright (c) 1998-2012                                                **
**  Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre          **
**                                                                         **
**  Copyright (c) 2010-2012                                                **
**  German Research School for Simulation Sciences GmbH,                   **
**  Laboratory for Parallel Programming                                    **
**                                                                         **
**  Copyright (c) 2003-2008                                                **
**  University of Tennessee, Innovative Computing Laboratory               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

/**
 * @file  epk_mpiwrap_p2p.c
 *
 * @brief C interface wrappers for point-to-point communication
 */

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

#include "elg_error.h"

#include "esd_event.h"

#include "epk_defs_mpi.h"
#include "epk_mpicom.h"
#include "epk_mpireq.h"
#include "epk_mpiwrap.h"

/** internal array of statuses */
static MPI_Status* my_status_array = NULL;
/** size of internal status array */
static int         my_status_array_size = 0;

/**
 * Get a pointer to a status array of at least 'size' statuses
 * @param  size minimal requested size
 * @return pointer to status array
 * @ingroup util
 */
static MPI_Status* epk_get_status_array(int size)
{
  if ((my_status_array_size == 0)
      && (size > 0))
  {
    /* -- never used: initialize -- */
    my_status_array = malloc(size * sizeof (MPI_Status));
    if (my_status_array == NULL)
    {
      elg_error();
    }
    my_status_array_size = size;
  }
  else
  if (size > my_status_array_size)
  {
    /* -- not enough room: expand -- */
    my_status_array = realloc(my_status_array, size * sizeof (MPI_Status));
    if (my_status_array == NULL)
    {
      elg_error();
    }
    my_status_array_size = size;
  }
  return my_status_array;
}

/**
 * @name Blocking
 * @{
 */
#if defined(HAS_MPI_BSEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Bsend
 * @note Auto-generated by wrapgen from template: ptp_send.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Bsend(void*        buf,
              int          count,
              MPI_Datatype datatype,
              int          dest,
              int          tag,
              MPI_Comm     comm)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int sz;

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_BSEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);
      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }
    return_val = PMPI_Bsend(buf, count, datatype, dest, tag, comm);

    esd_exit(epk_mpi_regid[EPK__MPI_BSEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Bsend(buf, count, datatype, dest, tag, comm);
  }

  return return_val;
} /* MPI_Bsend */

#endif
#if defined(HAS_MPI_RSEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Rsend
 * @note Auto-generated by wrapgen from template: ptp_send.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Rsend(void*        buf,
              int          count,
              MPI_Datatype datatype,
              int          dest,
              int          tag,
              MPI_Comm     comm)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int sz;

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_RSEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);
      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }
    return_val = PMPI_Rsend(buf, count, datatype, dest, tag, comm);

    esd_exit(epk_mpi_regid[EPK__MPI_RSEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Rsend(buf, count, datatype, dest, tag, comm);
  }

  return return_val;
} /* MPI_Rsend */

#endif
#if defined(HAS_MPI_SEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Send
 * @note Auto-generated by wrapgen from template: ptp_send.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Send(void*        buf,
             int          count,
             MPI_Datatype datatype,
             int          dest,
             int          tag,
             MPI_Comm     comm)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int sz;

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_SEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);
      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }
    return_val = PMPI_Send(buf, count, datatype, dest, tag, comm);

    esd_exit(epk_mpi_regid[EPK__MPI_SEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Send(buf, count, datatype, dest, tag, comm);
  }

  return return_val;
} /* MPI_Send */

#endif
#if defined(HAS_MPI_SSEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Ssend
 * @note Auto-generated by wrapgen from template: ptp_send.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Ssend(void*        buf,
              int          count,
              MPI_Datatype datatype,
              int          dest,
              int          tag,
              MPI_Comm     comm)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int sz;

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_SSEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);
      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }
    return_val = PMPI_Ssend(buf, count, datatype, dest, tag, comm);

    esd_exit(epk_mpi_regid[EPK__MPI_SSEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Ssend(buf, count, datatype, dest, tag, comm);
  }

  return return_val;
} /* MPI_Ssend */

#endif

#if defined(HAS_MPI_RECV) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Recv
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Recv(void*        buf,
             int          count,
             MPI_Datatype datatype,
             int          source,
             int          tag,
             MPI_Comm     comm,
             MPI_Status*  status)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int        sz;
    MPI_Status mystatus;

    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_RECV]);

    if (status == MPI_STATUS_IGNORE)
    {
      status = &mystatus;
    }
    return_val = PMPI_Recv(buf, count, datatype, source, tag, comm, status);

    if (source != MPI_PROC_NULL && return_val == MPI_SUCCESS)
    {
      PMPI_Type_size(datatype, &sz);
      PMPI_Get_count(status, datatype, &count);
      esd_mpi_recv(EPK_RANK(status->MPI_SOURCE),
                   EPK_COMM_ID(comm), status->MPI_TAG, count * sz);
    }

    esd_exit(epk_mpi_regid[EPK__MPI_RECV]);

    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Recv(buf, count, datatype, source, tag, comm, status);
  }

  return return_val;
} /* MPI_Recv */

#endif

#if defined(HAS_MPI_PROBE) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Probe
 * @note Auto-generated by wrapgen from template: std.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Probe(int         source,
              int         tag,
              MPI_Comm    comm,
              MPI_Status* status)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_PROBE]);

    return_val = PMPI_Probe(source, tag, comm, status);

    esd_exit(epk_mpi_regid[EPK__MPI_PROBE]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Probe(source, tag, comm, status);
  }

  return return_val;
}

#endif

#if defined(HAS_MPI_SENDRECV) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Sendrecv
 * @note Auto-generated by wrapgen from template: ptp_sendrecv.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Sendrecv(void*        sendbuf,
                 int          sendcount,
                 MPI_Datatype sendtype,
                 int          dest,
                 int          sendtag,
                 void*        recvbuf,
                 int          recvcount,
                 MPI_Datatype recvtype,
                 int          source,
                 int          recvtag,
                 MPI_Comm     comm,
                 MPI_Status*  status)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int        sendsz, recvsz;
    MPI_Status mystatus;

    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_SENDRECV]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(sendtype, &sendsz);
      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   sendtag, sendcount * sendsz);
    }

    if (status == MPI_STATUS_IGNORE)
    {
      status = &mystatus;
    }

    return_val = PMPI_Sendrecv(sendbuf,
                               sendcount,
                               sendtype,
                               dest,
                               sendtag,
                               recvbuf,
                               recvcount,
                               recvtype,
                               source,
                               recvtag,
                               comm,
                               status);

    if (source != MPI_PROC_NULL && return_val == MPI_SUCCESS)
    {
      PMPI_Type_size(recvtype, &recvsz);
      PMPI_Get_count(status, recvtype, &recvcount);
      esd_mpi_recv(EPK_RANK(status->MPI_SOURCE),
                   EPK_COMM_ID(comm), status->MPI_TAG, recvcount * recvsz);
    }

    esd_exit(epk_mpi_regid[EPK__MPI_SENDRECV]);

    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Sendrecv(sendbuf,
                               sendcount,
                               sendtype,
                               dest,
                               sendtag,
                               recvbuf,
                               recvcount,
                               recvtype,
                               source,
                               recvtag,
                               comm,
                               status);
  }

  return return_val;
} /* MPI_Sendrecv */

#endif

#if defined(HAS_MPI_SENDRECV_REPLACE) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Sendrecv_replace
 * @note Auto-generated by wrapgen from template: ptp_sendrecv.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Sendrecv_replace(void*        buf,
                         int          count,
                         MPI_Datatype datatype,
                         int          dest,
                         int          sendtag,
                         int          source,
                         int          recvtag,
                         MPI_Comm     comm,
                         MPI_Status*  status)
{
  int          return_val;
  int          sendcount = count, recvcount = count;
  MPI_Datatype sendtype  = datatype, recvtype = datatype;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int        sendsz, recvsz;
    MPI_Status mystatus;

    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_SENDRECV_REPLACE]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(sendtype, &sendsz);
      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   sendtag, sendcount * sendsz);
    }

    if (status == MPI_STATUS_IGNORE)
    {
      status = &mystatus;
    }

    return_val = PMPI_Sendrecv_replace(buf,
                                       count,
                                       datatype,
                                       dest,
                                       sendtag,
                                       source,
                                       recvtag,
                                       comm,
                                       status);

    if (source != MPI_PROC_NULL && return_val == MPI_SUCCESS)
    {
      PMPI_Type_size(recvtype, &recvsz);
      PMPI_Get_count(status, recvtype, &recvcount);
      esd_mpi_recv(EPK_RANK(status->MPI_SOURCE),
                   EPK_COMM_ID(comm), status->MPI_TAG, recvcount * recvsz);
    }

    esd_exit(epk_mpi_regid[EPK__MPI_SENDRECV_REPLACE]);

    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Sendrecv_replace(buf,
                                       count,
                                       datatype,
                                       dest,
                                       sendtag,
                                       source,
                                       recvtag,
                                       comm,
                                       status);
  }

  return return_val;
} /* MPI_Sendrecv_replace */

#endif

/**
 * @}
 * @name Non-blocking
 * @{
 */

#if defined(HAS_MPI_IBSEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Ibsend
 * @note Auto-generated by wrapgen from template: ptp_isend.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Ibsend(void*        buf,
               int          count,
               MPI_Datatype datatype,
               int          dest,
               int          tag,
               MPI_Comm     comm,
               MPI_Request* request)
{
  int       return_val;
  const int xnb_active = (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int     sz;
    elg_ui4 reqid = epk_get_request_id();

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_IBSEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);

      if (xnb_active)
      {
        esd_attr_ui4(ELG_ATTR_REQUEST, reqid);
      }

      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }

    return_val = PMPI_Ibsend(buf,
                             count,
                             datatype,
                             dest,
                             tag,
                             comm,
                             request);

    if (xnb_active && dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
    {
      epk_request_create(*request, ERF_SEND,
                         tag, dest, count * sz, datatype, comm, reqid);
    }

    esd_exit(epk_mpi_regid[EPK__MPI_IBSEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Ibsend(buf,
                             count,
                             datatype,
                             dest,
                             tag,
                             comm,
                             request);
  }

  return return_val;
} /* MPI_Ibsend */

#endif
#if defined(HAS_MPI_IRSEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Irsend
 * @note Auto-generated by wrapgen from template: ptp_isend.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Irsend(void*        buf,
               int          count,
               MPI_Datatype datatype,
               int          dest,
               int          tag,
               MPI_Comm     comm,
               MPI_Request* request)
{
  int       return_val;
  const int xnb_active = (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int     sz;
    elg_ui4 reqid = epk_get_request_id();

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_IRSEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);

      if (xnb_active)
      {
        esd_attr_ui4(ELG_ATTR_REQUEST, reqid);
      }

      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }

    return_val = PMPI_Irsend(buf,
                             count,
                             datatype,
                             dest,
                             tag,
                             comm,
                             request);

    if (xnb_active && dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
    {
      epk_request_create(*request, ERF_SEND,
                         tag, dest, count * sz, datatype, comm, reqid);
    }

    esd_exit(epk_mpi_regid[EPK__MPI_IRSEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Irsend(buf,
                             count,
                             datatype,
                             dest,
                             tag,
                             comm,
                             request);
  }

  return return_val;
} /* MPI_Irsend */

#endif
#if defined(HAS_MPI_ISEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Isend
 * @note Auto-generated by wrapgen from template: ptp_isend.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Isend(void*        buf,
              int          count,
              MPI_Datatype datatype,
              int          dest,
              int          tag,
              MPI_Comm     comm,
              MPI_Request* request)
{
  int       return_val;
  const int xnb_active = (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int     sz;
    elg_ui4 reqid = epk_get_request_id();

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_ISEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);

      if (xnb_active)
      {
        esd_attr_ui4(ELG_ATTR_REQUEST, reqid);
      }

      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }

    return_val = PMPI_Isend(buf, count, datatype, dest, tag, comm, request);

    if (xnb_active && dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
    {
      epk_request_create(*request, ERF_SEND,
                         tag, dest, count * sz, datatype, comm, reqid);
    }

    esd_exit(epk_mpi_regid[EPK__MPI_ISEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Isend(buf, count, datatype, dest, tag, comm, request);
  }

  return return_val;
} /* MPI_Isend */

#endif
#if defined(HAS_MPI_ISSEND) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Issend
 * @note Auto-generated by wrapgen from template: ptp_isend.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Issend(void*        buf,
               int          count,
               MPI_Datatype datatype,
               int          dest,
               int          tag,
               MPI_Comm     comm,
               MPI_Request* request)
{
  int       return_val;
  const int xnb_active = (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    int     sz;
    elg_ui4 reqid = epk_get_request_id();

    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_ISSEND]);

    if (dest != MPI_PROC_NULL)
    {
      PMPI_Type_size(datatype, &sz);

      if (xnb_active)
      {
        esd_attr_ui4(ELG_ATTR_REQUEST, reqid);
      }

      esd_mpi_send(EPK_RANK(dest), EPK_COMM_ID(comm),
                   tag, count * sz);
    }

    return_val = PMPI_Issend(buf,
                             count,
                             datatype,
                             dest,
                             tag,
                             comm,
                             request);

    if (xnb_active && dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
    {
      epk_request_create(*request, ERF_SEND,
                         tag, dest, count * sz, datatype, comm, reqid);
    }

    esd_exit(epk_mpi_regid[EPK__MPI_ISSEND]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Issend(buf,
                             count,
                             datatype,
                             dest,
                             tag,
                             comm,
                             request);
  }

  return return_val;
} /* MPI_Issend */

#endif

#if defined(HAS_MPI_IRECV) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Irecv
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Irecv(void*        buf,
              int          count,
              MPI_Datatype datatype,
              int          source,
              int          tag,
              MPI_Comm     comm,
              MPI_Request* request)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int xnb_active       =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);
  int       return_val;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_IRECV]);
  }

  return_val = PMPI_Irecv(buf, count, datatype, source, tag, comm, request);

  if (source != MPI_PROC_NULL && return_val == MPI_SUCCESS)
  {
    elg_ui4 reqid = epk_get_request_id();

    int     sz;
    PMPI_Type_size(datatype, &sz);

    if (event_gen_active && xnb_active)
    {
      esd_mpi_recv_request(reqid);
    }

    epk_request_create(*request, ERF_RECV,
                       tag, 0, count * sz, datatype, comm, reqid);
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_IRECV]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Irecv */

#endif

#if defined(HAS_MPI_IPROBE) && !defined(NO_MPI_EXTRA) \
  && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Iprobe
 * @note Auto-generated by wrapgen from template: std.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Iprobe(int         source,
               int         tag,
               MPI_Comm    comm,
               int*        flag,
               MPI_Status* status)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_IPROBE]);

    return_val = PMPI_Iprobe(source, tag, comm, flag, status);

    esd_exit(epk_mpi_regid[EPK__MPI_IPROBE]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Iprobe(source, tag, comm, flag, status);
  }

  return return_val;
}

#endif

#if defined(HAS_MPI_WAIT) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Wait
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Wait(MPI_Request* request,
             MPI_Status*  status)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int                return_val;
  MPI_Status         mystatus;
  struct EpkRequest* orig_req;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_WAIT]);
  }

  if (status == MPI_STATUS_IGNORE)
  {
    status = &mystatus;
  }

  orig_req   = epk_request_get(*request);
  return_val = PMPI_Wait(request, status);
  epk_check_request(orig_req, status);

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_WAIT]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Wait */

#endif

#if defined(HAS_MPI_WAITALL) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Waitall
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Waitall(int          count,
                MPI_Request* requests,
                MPI_Status*  array_of_statuses)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int                return_val, i;
  struct EpkRequest* orig_req;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_WAITALL]);
  }

  #if defined (HAS_MPI_STATUSES_IGNORE)
  if (array_of_statuses == MPI_STATUSES_IGNORE)
  {
    /* allocate status array for internal use */
    array_of_statuses = epk_get_status_array(count);
  }
  #endif

  epk_save_request_array(requests, count);

  return_val = PMPI_Waitall(count, requests, array_of_statuses);

  for (i = 0; i < count; i++)
  {
    orig_req = epk_saved_request_get(i);
    epk_check_request(orig_req, &(array_of_statuses[i]));
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_WAITALL]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Waitall */

#endif

#if defined(HAS_MPI_WAITANY) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Waitany
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Waitany(int          count,
                MPI_Request* requests,
                int*         index,
                MPI_Status*  status)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int          xtest_active     =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XREQTEST);
  int                return_val;
  struct EpkRequest* orig_req;
  MPI_Status         mystatus;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_WAITANY]);
  }

  if (status == MPI_STATUS_IGNORE)
  {
    status = &mystatus;
  }

  epk_save_request_array(requests, count);

  return_val = PMPI_Waitany(count, requests, index, status);

  if (event_gen_active && xtest_active)
  {
    int i;

    for (i = 0; i < count; ++i)
    {
      orig_req = epk_saved_request_get(i);

      if (i == *index)
      {
        epk_check_request(orig_req, status);
      }
      else
      if (orig_req)
      {
        esd_mpi_request_tested(orig_req->id);
      }
    }
  }
  else
  {
    orig_req = epk_saved_request_get(*index);
    epk_check_request(orig_req, status);
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_WAITANY]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Waitany */

#endif

#if defined(HAS_MPI_WAITSOME) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Waitsome
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Waitsome(int          incount,
                 MPI_Request* array_of_requests,
                 int*         outcount,
                 int*         array_of_indices,
                 MPI_Status*  array_of_statuses)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int          xtest_active     =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XREQTEST);
  int                return_val, i;
  struct EpkRequest* orig_req;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_WAITSOME]);
  }

  #if defined(HAS_MPI_STATUSES_IGNORE)
  if (array_of_statuses == MPI_STATUSES_IGNORE)
  {
    /* allocate status array for internal use */
    array_of_statuses = epk_get_status_array(incount);
  }
  #endif

  epk_save_request_array(array_of_requests, incount);

  return_val = PMPI_Waitsome(incount, array_of_requests, outcount,
                             array_of_indices, array_of_statuses);

  if (event_gen_active && xtest_active)
  {
    int        j, tmp, cur;
    MPI_Status tmpstat;

    cur = 0;

    for (i = 0; i < incount; ++i)
    {
      orig_req = epk_saved_request_get(i);

      if (orig_req)
      {
        for (j = cur; j < *outcount && i != array_of_indices[j]; ++j)
        {
          ;
        }

        if (j < *outcount)
        {
          tmpstat = array_of_statuses[cur];
          epk_check_request(orig_req, &(array_of_statuses[cur]));
          array_of_statuses[j] = tmpstat;

          tmp                   = array_of_indices[cur];
          array_of_indices[cur] = array_of_indices[j];
          array_of_indices[j]   = tmp;

          ++cur;
        }
        else
        {
          esd_mpi_request_tested(orig_req->id);
        }
      }
    }
  }
  else
  {
    for (i = 0; i < *outcount; ++i)
    {
      orig_req = epk_saved_request_get(array_of_indices[i]);
      epk_check_request(orig_req, &(array_of_statuses[i]));
    }
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_WAITSOME]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Waitsome */

#endif

#if defined(HAS_MPI_TEST) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Test
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Test(MPI_Request* request,
             int*         flag,
             MPI_Status*  status)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int          xtest_active     =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XREQTEST);
  int                return_val;
  struct EpkRequest* orig_req;
  MPI_Status         mystatus;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_TEST]);
  }

  if (status == MPI_STATUS_IGNORE)
  {
    status = &mystatus;
  }

  orig_req   = epk_request_get(*request);
  return_val = PMPI_Test(request, flag, status);

  if (*flag)
  {
    epk_check_request(orig_req, status);
  }
  else
  if (orig_req && event_gen_active && xtest_active)
  {
    esd_mpi_request_tested(orig_req->id);
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_TEST]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Test */

#endif

#if defined(HAS_MPI_TESTANY) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Testany
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Testany(int          count,
                MPI_Request* array_of_requests,
                int*         index,
                int*         flag,
                MPI_Status*  status)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int          xtest_active     =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XREQTEST);
  int                return_val;
  struct EpkRequest* orig_req;
  MPI_Status         mystatus;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_TESTANY]);
  }

  if (status == MPI_STATUS_IGNORE)
  {
    status = &mystatus;
  }
  epk_save_request_array(array_of_requests, count);
  return_val = PMPI_Testany(count, array_of_requests, index, flag, status);

  if (event_gen_active && xtest_active)
  {
    int i;

    for (i = 0; i < count; ++i)
    {
      orig_req = epk_saved_request_get(i);

      if (*index == i)
      {
        epk_check_request(orig_req, status);
      }
      else
      if (orig_req)
      {
        esd_mpi_request_tested(orig_req->id);
      }
    }
  }
  else
  if (*flag && *index != MPI_UNDEFINED)
  {
    orig_req = epk_saved_request_get(*index);
    epk_check_request(orig_req, status);
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_TESTANY]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Testany */

#endif

#if defined(HAS_MPI_TESTALL) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Testall
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Testall(int          count,
                MPI_Request* array_of_requests,
                int*         flag,
                MPI_Status*  array_of_statuses)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int          xtest_active     =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XREQTEST);
  int                return_val, i;
  struct EpkRequest* orig_req;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_TESTALL]);
  }

  #if defined(HAS_MPI_STATUSES_IGNORE)
  if (array_of_statuses == MPI_STATUSES_IGNORE)
  {
    /* allocate status array for internal use */
    array_of_statuses = epk_get_status_array(count);
  }
  #endif

  epk_save_request_array(array_of_requests, count);

  return_val = PMPI_Testall(count,
                            array_of_requests,
                            flag,
                            array_of_statuses);

  if (*flag)
  {
    for (i = 0; i < count; i++)
    {
      orig_req = epk_saved_request_get(i);
      epk_check_request(orig_req, &(array_of_statuses[i]));
    }
  }
  else
  if (event_gen_active && xtest_active)
  {
    int i;

    for (i = 0; i < count; i++)
    {
      orig_req = epk_saved_request_get(i);
      if (orig_req)
      {
        esd_mpi_request_tested(orig_req->id);
      }
    }
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_TESTALL]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Testall */

#endif

#if defined(HAS_MPI_TESTSOME) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Testsome
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Testsome(int          incount,
                 MPI_Request* array_of_requests,
                 int*         outcount,
                 int*         array_of_indices,
                 MPI_Status*  array_of_statuses)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int          xtest_active     =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XREQTEST);
  int                return_val, i;
  struct EpkRequest* orig_req;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_TESTSOME]);
  }

  #if defined(HAS_MPI_STATUSES_IGNORE)
  if (array_of_statuses == MPI_STATUSES_IGNORE)
  {
    /* allocate status array for internal use */
    array_of_statuses = epk_get_status_array(incount);
  }
  #endif

  epk_save_request_array(array_of_requests, incount);
  return_val = PMPI_Testsome(incount, array_of_requests, outcount,
                             array_of_indices, array_of_statuses);

  if (event_gen_active && xtest_active)
  {
    int        cur, j, tmp;
    MPI_Status tmpstat;

    cur = 0;

    for (i = 0; i < incount; ++i)
    {
      orig_req = epk_saved_request_get(i);

      if (orig_req)
      {
        for (j = cur; j < *outcount && i != array_of_indices[j]; ++j)
        {
          ;
        }

        if (j < *outcount)
        {
          tmpstat = array_of_statuses[cur];
          epk_check_request(orig_req, &(array_of_statuses[cur]));
          array_of_statuses[j] = tmpstat;

          tmp                   = array_of_indices[cur];
          array_of_indices[cur] = array_of_indices[j];
          array_of_indices[j]   = tmp;

          ++cur;
        }
        else
        {
          esd_mpi_request_tested(orig_req->id);
        }
      }
    }
  }
  else
  {
    for (i = 0; i < *outcount; ++i)
    {
      orig_req = epk_saved_request_get(array_of_indices[i]);
      epk_check_request(orig_req, &(array_of_statuses[i]));
    }
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_TESTSOME]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Testsome */

#endif

/**
 * @}
 * @name Persitent requests
 * @{
 */

#if defined(HAS_MPI_BSEND_INIT) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Bsend_init
 * @note Auto-generated by wrapgen from template: ptp_sendinit.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Bsend_init(void*        buf,
                   int          count,
                   MPI_Datatype datatype,
                   int          dest,
                   int          tag,
                   MPI_Comm     comm,
                   MPI_Request* request)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int       return_val, sz;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_BSEND_INIT]);
  }

  PMPI_Type_size(datatype, &sz);

  return_val = PMPI_Bsend_init(buf,
                               count,
                               datatype,
                               dest,
                               tag,
                               comm,
                               request);
  if (dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
  {
    epk_request_create(*request, (ERF_SEND | ERF_IS_PERSISTENT),
                       tag, dest, count * sz, datatype, comm,
                       epk_get_request_id());
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_BSEND_INIT]);
    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Bsend_init */

#endif
#if defined(HAS_MPI_RSEND_INIT) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Rsend_init
 * @note Auto-generated by wrapgen from template: ptp_sendinit.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Rsend_init(void*        buf,
                   int          count,
                   MPI_Datatype datatype,
                   int          dest,
                   int          tag,
                   MPI_Comm     comm,
                   MPI_Request* request)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int       return_val, sz;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_RSEND_INIT]);
  }

  PMPI_Type_size(datatype, &sz);

  return_val = PMPI_Rsend_init(buf,
                               count,
                               datatype,
                               dest,
                               tag,
                               comm,
                               request);
  if (dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
  {
    epk_request_create(*request, (ERF_SEND | ERF_IS_PERSISTENT),
                       tag, dest, count * sz, datatype, comm,
                       epk_get_request_id());
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_RSEND_INIT]);
    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Rsend_init */

#endif
#if defined(HAS_MPI_SEND_INIT) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Send_init
 * @note Auto-generated by wrapgen from template: ptp_sendinit.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Send_init(void*        buf,
                  int          count,
                  MPI_Datatype datatype,
                  int          dest,
                  int          tag,
                  MPI_Comm     comm,
                  MPI_Request* request)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int       return_val, sz;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_SEND_INIT]);
  }

  PMPI_Type_size(datatype, &sz);

  return_val = PMPI_Send_init(buf,
                              count,
                              datatype,
                              dest,
                              tag,
                              comm,
                              request);
  if (dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
  {
    epk_request_create(*request, (ERF_SEND | ERF_IS_PERSISTENT),
                       tag, dest, count * sz, datatype, comm,
                       epk_get_request_id());
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_SEND_INIT]);
    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Send_init */

#endif
#if defined(HAS_MPI_SSEND_INIT) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Ssend_init
 * @note Auto-generated by wrapgen from template: ptp_sendinit.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Ssend_init(void*        buf,
                   int          count,
                   MPI_Datatype datatype,
                   int          dest,
                   int          tag,
                   MPI_Comm     comm,
                   MPI_Request* request)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int       return_val, sz;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_SSEND_INIT]);
  }

  PMPI_Type_size(datatype, &sz);

  return_val = PMPI_Ssend_init(buf,
                               count,
                               datatype,
                               dest,
                               tag,
                               comm,
                               request);
  if (dest != MPI_PROC_NULL && return_val == MPI_SUCCESS)
  {
    epk_request_create(*request, (ERF_SEND | ERF_IS_PERSISTENT),
                       tag, dest, count * sz, datatype, comm,
                       epk_get_request_id());
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_SSEND_INIT]);
    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Ssend_init */

#endif

#if defined(HAS_MPI_RECV_INIT) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Recv_init
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Recv_init(void*        buf,
                  int          count,
                  MPI_Datatype datatype,
                  int          source,
                  int          tag,
                  MPI_Comm     comm,
                  MPI_Request* request)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int       return_val;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_RECV_INIT]);
  }

  return_val = PMPI_Recv_init(buf,
                              count,
                              datatype,
                              source,
                              tag,
                              comm,
                              request);
  if (source != MPI_PROC_NULL && return_val == MPI_SUCCESS)
  {
    int sz;
    PMPI_Type_size(datatype, &sz);
    epk_request_create(*request, (ERF_RECV | ERF_IS_PERSISTENT),
                       tag, source, count * sz, datatype, comm,
                       epk_get_request_id());
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_RECV_INIT]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Recv_init */

#endif

#if defined(HAS_MPI_START) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Start
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Start(MPI_Request* request)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int xnb_active       =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);
  int       return_val;

  if (event_gen_active)
  {
    struct EpkRequest* req;

    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_START]);

    req = epk_request_get(*request);
    if (req && (req->flags & ERF_IS_PERSISTENT))
    {
      req->flags |= ERF_IS_ACTIVE;
      if ((req->flags & ERF_SEND) && (req->dest != MPI_PROC_NULL))
      {
        if (xnb_active)
        {
          esd_attr_ui4(ELG_ATTR_REQUEST, req->id);
        }

        esd_mpi_send(EPK_RANK(req->dest),
                     EPK_COMM_ID(req->comm), req->tag,  req->bytes);
      }
      else
      if (req->flags & ERF_RECV && xnb_active)
      {
        esd_mpi_recv_request(req->id);
      }
    }
  }

  return_val = PMPI_Start(request);

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_START]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Start */

#endif

#if defined(HAS_MPI_STARTALL) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Startall
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Startall(int          count,
                 MPI_Request* array_of_requests)
{
  const int event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int xnb_active       =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);
  int       return_val, i;

  if (event_gen_active)
  {
    MPI_Request*       request;
    struct EpkRequest* req;

    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_STARTALL]);

    for (i = 0; i < count; i++)
    {
      request = &array_of_requests[i];
      req     = epk_request_get(*request);

      if (req && (req->flags & ERF_IS_PERSISTENT))
      {
        req->flags |= ERF_IS_ACTIVE;
        if ((req->flags & ERF_SEND) && (req->dest != MPI_PROC_NULL))
        {
          if (xnb_active)
          {
            esd_attr_ui4(ELG_ATTR_REQUEST, req->id);
          }

          esd_mpi_send(EPK_RANK(req->dest),
                       EPK_COMM_ID(req->comm), req->tag,  req->bytes);
        }
        else
        if (req->flags & ERF_RECV && xnb_active)
        {
          esd_mpi_recv_request(req->id);
        }
      }
    }
  }

  return_val = PMPI_Startall(count, array_of_requests);

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_STARTALL]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Startall */

#endif

#if defined(HAS_MPI_REQUEST_FREE) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Request_free
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Request_free(MPI_Request* request)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  const int          xnb_active       =
    (epk_mpi_enabled & EPK_MPI_ENABLED_XNONBLOCK);
  int                orig_req_null = (*request == MPI_REQUEST_NULL);
  int                return_val;
  struct EpkRequest* req;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_REQUEST_FREE]);
  }

  req = epk_request_get(*request);

  if (req)
  {
    if (req->flags & ERF_CAN_CANCEL && event_gen_active && xnb_active)
    {
      MPI_Status status;
      int        cancelled;

      /* -- Must check if request was cancelled and write the
       *    cancel event. Not doing so will confuse the trace
       *    analysis.
       */

      return_val = PMPI_Wait(request, &status);
      PMPI_Test_cancelled(&status, &cancelled);

      if (cancelled)
      {
        esd_mpi_cancelled(req->id);
      }
    }

    if ((req->flags & ERF_IS_PERSISTENT) && (req->flags & ERF_IS_ACTIVE))
    {
      /* mark active requests for deallocation */
      req->flags |= ERF_DEALLOCATE;
    }
    else
    {
      /* deallocate inactive requests -*/
      epk_request_free(req);
    }
  }

  /* -- We had to call PMPI_Wait for cancellable requests, which already
   *    frees (non-persistent) requests itself and sets them to
   *    MPI_REQUEST_NULL.
   *    As MPI_Request_free does not really like being called with
   *    MPI_REQUEST_NULL, we have to catch this situation here and only
   *    pass MPI_REQUEST_NULL if the application explicitely wanted that
   *    for some reason.
   */
  if (*request != MPI_REQUEST_NULL || orig_req_null)
  {
    return_val = PMPI_Request_free(request);
  }

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_REQUEST_FREE]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Request_free */

#endif

#if defined(HAS_MPI_CANCEL) && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Cancel
 * @note Manually adapted wrapper
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Cancel(MPI_Request* request)
{
  const int          event_gen_active = IS_EVENT_GEN_ON_FOR(P2P);
  int                return_val;
  struct EpkRequest* req;

  if (event_gen_active)
  {
    EVENT_GEN_OFF();

    esd_enter(epk_mpi_regid[EPK__MPI_CANCEL]);
  }

  /* Mark request as cancellable and check for successful cancellation
   * on request completion or MPI_Request_free.
   * If XNONBLOCK is enabled, there will be a 'cancelled' event
   * instead of a normal completion event in the trace, which can be
   * checked for by the trace analysis.
   */

  req = epk_request_get(*request);

  if (req)
  {
    req->flags |= ERF_CAN_CANCEL;
  }

  return_val = PMPI_Cancel(request);

  if (event_gen_active)
  {
    esd_exit(epk_mpi_regid[EPK__MPI_CANCEL]);

    EVENT_GEN_ON();
  }

  return return_val;
} /* MPI_Cancel */

#endif

#if defined(HAS_MPI_TEST_CANCELLED) && !defined(NO_MPI_EXTRA) \
  && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Test_cancelled
 * @note Auto-generated by wrapgen from template: std.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Test_cancelled(MPI_Status* status,
                       int*        flag)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_TEST_CANCELLED]);

    return_val = PMPI_Test_cancelled(status, flag);

    esd_exit(epk_mpi_regid[EPK__MPI_TEST_CANCELLED]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Test_cancelled(status, flag);
  }

  return return_val;
}

#endif

/**
 * @}
 * @name Auxiluary functions
 * @{
 */

#if defined(HAS_MPI_BUFFER_ATTACH) && !defined(NO_MPI_EXTRA) \
  && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Buffer_attach
 * @note Auto-generated by wrapgen from template: std.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Buffer_attach(void* buffer,
                      int   size)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_BUFFER_ATTACH]);

    return_val = PMPI_Buffer_attach(buffer, size);

    esd_exit(epk_mpi_regid[EPK__MPI_BUFFER_ATTACH]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Buffer_attach(buffer, size);
  }

  return return_val;
}

#endif
#if defined(HAS_MPI_BUFFER_DETACH) && !defined(NO_MPI_EXTRA) \
  && !defined(NO_MPI_P2P)
/**
 * Measurement wrapper for MPI_Buffer_detach
 * @note Auto-generated by wrapgen from template: std.w
 * @note C interface
 * @note Introduced with MPI 1.0
 * @ingroup p2p
 */
int MPI_Buffer_detach(void* buffer,
                      int*  size)
{
  int return_val;

  if (IS_EVENT_GEN_ON_FOR(P2P))
  {
    EVENT_GEN_OFF();
    esd_enter(epk_mpi_regid[EPK__MPI_BUFFER_DETACH]);

    return_val = PMPI_Buffer_detach(buffer, size);

    esd_exit(epk_mpi_regid[EPK__MPI_BUFFER_DETACH]);
    EVENT_GEN_ON();
  }
  else
  {
    return_val = PMPI_Buffer_detach(buffer, size);
  }

  return return_val;
}

#endif

/**
 * @}
 */
