/* -*- Mode: c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
 *  (C) 2011 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */

/* this file should be included by the using device's mpidimpl.h */

#ifndef MPID_SCHED_H_INCLUDED
#define MPID_SCHED_H_INCLUDED

/* FIXME open questions:
 * - Should the schedule hold a pointer to the nbc request and the nbc request
 *   hold a pointer to the schedule?  This could cause MT issues.
 */


enum MPIDU_Sched_entry_type {
    MPIDU_SCHED_ENTRY_INVALID_LB = 0,
    MPIDU_SCHED_ENTRY_SEND,
    /* _SEND_DEFER is easily implemented via _SEND */
    MPIDU_SCHED_ENTRY_RECV,
    /* _RECV_STATUS is easily implemented via _RECV */
    MPIDU_SCHED_ENTRY_REDUCE,
    MPIDU_SCHED_ENTRY_COPY,
    MPIDU_SCHED_ENTRY_NOP,
    MPIDU_SCHED_ENTRY_CB,
    MPIDU_SCHED_ENTRY_INVALID_UB
};

struct MPIDU_Sched_send {
    const void *buf;
    MPI_Aint count;
    const MPI_Aint *count_p;
    MPI_Datatype datatype;
    int dest;
    MPID_Comm *comm;
    MPID_Request *sreq;
    int is_sync; /* TRUE iff this send is an ssend */
};

struct MPIDU_Sched_recv {
    void *buf;
    MPI_Aint count;
    MPI_Datatype datatype;
    int src;
    MPID_Comm *comm;
    MPID_Request *rreq;
    MPI_Status *status;
};

struct MPIDU_Sched_reduce {
    const void *inbuf;
    void *inoutbuf;
    MPI_Aint count;
    MPI_Datatype datatype;
    MPI_Op op;
};

struct MPIDU_Sched_copy {
    const void *inbuf;
    MPI_Aint incount;
    MPI_Datatype intype;
    void *outbuf;
    MPI_Aint outcount;
    MPI_Datatype outtype;
};

/* nop entries have no args, so no structure is needed */

enum MPIDU_Sched_cb_type {
    MPIDU_SCHED_CB_TYPE_1 = 0, /* single state arg type --> MPID_Sched_cb_t */
    MPIDU_SCHED_CB_TYPE_2      /* double state arg type --> MPID_Sched_cb2_t */
};

struct MPIDU_Sched_cb {
    enum MPIDU_Sched_cb_type cb_type;
    union {
        MPID_Sched_cb_t *cb_p;
        MPID_Sched_cb2_t *cb2_p;
    } u;
    void *cb_state;
    void *cb_state2; /* unused for single-param callbacks */
};

enum MPIDU_Sched_entry_status {
    MPIDU_SCHED_ENTRY_STATUS_NOT_STARTED = 0,
    MPIDU_SCHED_ENTRY_STATUS_STARTED,
    MPIDU_SCHED_ENTRY_STATUS_COMPLETE,
    MPIDU_SCHED_ENTRY_STATUS_FAILED, /* indicates a failure occurred while executing the entry */
    MPIDU_SCHED_ENTRY_STATUS_INVALID /* indicates an invalid entry, or invalid status value */
};

/* Use a tagged union for schedule entries.  Not always space optimal, but saves
 * lots of error-prone pointer arithmetic and makes scanning the schedule easy. */
struct MPIDU_Sched_entry {
    enum MPIDU_Sched_entry_type type;
    enum MPIDU_Sched_entry_status status;
    int is_barrier;
    union {
        struct MPIDU_Sched_send send;
        struct MPIDU_Sched_recv recv;
        struct MPIDU_Sched_reduce reduce;
        struct MPIDU_Sched_copy copy;
        /* nop entries have no args */
        struct MPIDU_Sched_cb cb;
    } u;
};

struct MPIDU_Sched {
    size_t size; /* capacity (in entries) of the entries array */
    size_t idx;  /* index into entries array of first yet-outstanding entry */
    int num_entries; /* number of populated entries, num_entries <= size */
    int tag;
    MPID_Request *req; /* really needed? could cause MT problems... */
    struct MPIDU_Sched_entry *entries;

    struct MPIDU_Sched *next; /* linked-list next pointer */
    struct MPIDU_Sched *prev; /* linked-list next pointer */
};

/* prototypes */
int MPIDU_Sched_progress(int *made_progress);
int MPIDU_Sched_are_pending(void);

#endif /* !defined(MPID_SCHED_H_INCLUDED) */
