/*
 * mp_inx.h
 *
 * Intel Paragon INX protocol configuration for pr_mp.c
 *
 * rcsid = "$Header: /home/globdev/CVS/globus-packages/mp/source/globus_mp_inx.h,v 1.47 2002/04/18 17:25:34 bresnaha Exp $"
 */

#ifndef GLOBUS_INCLUDE_GLOBUS_MP_INX
#define GLOBUS_INCLUDE_GLOBUS_MP_INX


#ifndef EXTERN_C_BEGIN
#    ifdef __cplusplus
#        define EXTERN_C_BEGIN extern "C" {
#        define EXTERN_C_END }
#    else
#        define EXTERN_C_BEGIN
#        define EXTERN_C_END
#    endif
#endif

EXTERN_C_BEGIN

#undef GLOBUS_MP_HAS_INX_PROTO

/* #ifdef GLOBUS_MP_HAS_INX_PROTO
 * XXXXX all of this code is probably broken... remove this
 * GLOBUS_INCLUDE_GLOBUS_MP_INX_FOO line when it is fixed
 */
#ifdef GLOBUS_MP_HAS_INX_PROTO_FOO

/* nexus uses HAVE_INX_PROTO */
#define HAVE_INX_PROTO 1
#define GLOBUS_USING_THIS_MP_PROTO  1

#define NEXUS_MP_PROTO_INX
#define NEXUS_PROTO_TYPE_MP NEXUS_PROTO_TYPE_INX
#define NEXUS_PROTO_NAME_MP "inx"
#define MP_PROTOCOL_INFO _nx_pr_inx_info

#define GLOBUS_NEXUS_MP_PROTOCOL_INFO      globus_i_nexus_pr_inx_info
#define GLOBUS_NEXUS_MP_PROTOCOL_TYPE      globus_i_nexus_pr_inx_type
#define GLOBUS_NEXUS_MP_PROTOCOL_COUNT     globus_i_nexus_pr_inx_count
#define GLOBUS_NEXUS_MP_PROTOCOL_SP_MATCH  globus_i_nexus_pr_inx_sp_match

#define MP_STARTUP_INFO _nx_st_inx_info


/*
 * MP_PROTO_IS_THREAD_SAFE
 *
 * It is advantageous if an MP module be thread safe, since this
 * allows you to simply create a handler thread which blocks on a
 * receive, and automatically wakes up and handles the message when
 * one arrives.
 *
 * In order for an MP module to be considered "thread safe", the
 * following must be true:
 *   1) One thread can be doing a blocking receive, while other threads
 *	are simultaneously doing sends.
 *   2) A thread can send a message to its own node, which will
 *	be received by the blocking receive in the handler thread.
 *
 * If these conditions are true, then #define MP_PROTO_IS_THREAD_SAFE
 */
#ifndef BUILD_LITE
#define MP_PROTO_IS_THREAD_SAFE
#endif

/*
 * mp_destination_t
 *
 * This encapsulates the information about a node to which we
 * may send messages.
 */
typedef struct _mp_destination_t
{
    long node;
    long ptype;
} mp_destination_t;

/* Desribe contents of mp_destination_t for nexus_mi_proto_t manipulation */
#define MI_INT_COUNT 2
#define MI_STR_COUNT 1

/*
 * mp_send_status_t
 * mp_receive_status_t
 */
typedef int mp_send_status_t;
typedef long mp_receive_status_t;

#define INX_MSG_TYPE		0
#define INX_MSG_ACK_TYPE	1

/*
 * MP_DEFAULT_STORAGE_SIZE
 *
 * This is the default buffer size that will be used on sends and
 * receives to avoid extraneous mallocs and locks.
 */
#define MP_DEFAULT_STORAGE_SIZE 32736

/*
 * MP_BIG_MESSAGE_MIN_SIZE
 *
 * Any message >= to this size will be sent using a two message
 * protocol.  A first, small message is sent with the size,
 * followed by a second message with the actual data.
 */
#define MP_BIG_MESSAGE_MIN_SIZE 1500

/*
 * MP_INITIALIZE()
 *
 * Do any initialization required before calling any other MP_ functions.
 */
#define MP_INITIALIZE()

/*
 * MP_INIT_NODE_INFO(mp_destination_t Mynode, int N_nodes)
 *
 * Set Mynode to the mp_destionation_t info for my node.
 * Set N_nodes the the number of nodes.
 */
#define MP_INIT_NODE_INFO(My_node, N_nodes) \
{ \
    (My_node).node = mynode(); \
    (My_node).ptype = myptype(); \
    N_nodes = numnodes(); \
}

/*
 * MP_IS_MASTER_NODE(mp_destination_t dest, nexus_bool_t result)
 */
#define MP_IS_MASTER_NODE(dest, result) \
{ \
    if ((dest).node == 0) \
	result = NEXUS_TRUE; \
    else \
	result = NEXUS_FALSE; \
}

/*
 * MP_SET_DESTINATION(mp_destination_t dest, int node_num)
 */
#define MP_SET_DESTINATION(dest, node_num) \
{ \
    (dest).node = (node_num); \
    (dest).ptype = 0; \
}

/*
 * MP_POST_RECEIVE(Func,
 *		   nexus_byte_t *Buf,
 *		   unsigned long Buf_size,
 *		   mp_receive_status_t Status)
 *
 * Post a receive into Buf with a maximum size of Buf_size.
 * MP_RECEIVE_STATUS() and MP_RECEIVE_WAIT() should be used
 * with Status to determine completion of the receive.
 */
#define MP_POST_RECEIVE(Func, Buf, Buf_size, Status) \
{ \
    if (((Status) = _irecv(INX_MSG_TYPE, \
			   (char *) (Buf), \
			   (long) (Buf_size))) == -1) \
    { \
	int save_errno = errno; \
	nexus_fatal("MP_POST_RECEIVE(): _irecv() failed, errno=%d, %s\n", save_errno, _nx_md_system_error_string(save_errno)); \
    } \
}


/*
 * MP_RECEIVE_STATUS(Func,
 *                   mp_receive_status_t Status,
 *                   nexus_bool_t Done)
 *
 * See if the receive described by Status has completed.  If so, then
 * set Done=NEXUS_TRUE. else set Done=NEXUS_FALSE.
 */
#define MP_RECEIVE_STATUS(Func, Status, Done) \
{ \
    long inx_rc; \
    if ((inx_rc = _msgdone(Status)) == 1) \
    { \
	(Done) = NEXUS_TRUE; \
    } \
    else if (inx_rc == 0) \
    { \
	(Done) = NEXUS_FALSE; \
    } \
    else \
    { \
	int save_errno = errno; \
	nexus_fatal("MP_RECEIVE_STATUS(): _msgdone() failed, errno=%d, %s\n", save_errno, _nx_md_system_error_string(save_errno)); \
    } \
}


/*
 * MP_RECEIVE_WAIT(Func,
 *                 mp_receive_status_t Status,
 *                 nexus_bool_t Done)
 *
 * Wait for the receive described by Status to complete.
 * If it completes successfully, then
 * set Done=NEXUS_TRUE. else set Done=NEXUS_FALSE.
 */
#define MP_RECEIVE_WAIT(Func, Status, Done) \
{ \
    if (_msgwait(Status) == 0) \
    { \
	(Done) = NEXUS_TRUE; \
    } \
    else \
    { \
	int save_errno = errno; \
	nexus_fatal("MP_RECEIVE_WAIT(): _msgwait() failed, errno=%d, %s\n", save_errno, _nx_md_system_error_string(save_errno)); \
    } \
}

/*
 * MP_RECEIVE_CANCEL(mp_receive_status_t Status)
 *
 * Cancel a previously posted receive.
 */
#define MP_RECEIVE_CANCEL(Status)

/*
 * MP_SEND(mp_destination_t Dest,
 *         nexus_byte_t *Buf,
 *         unsigned long Buf_size,
 *         mp_send_status_t Status)
 *
 * Send Buf, of size Buf_size, to Dest.
 * The completion of the send will be checked by calling
 * MP_SEND_STATUS() using the Status argument.
 */
#define MP_SEND(Dest, Buf, Buf_size, Status) \
{ \
    if (((Status) = _isend(INX_MSG_TYPE, \
			   (char *) (Buf), \
			   (long) (Buf_size), \
			   (Dest).node, \
			   (Dest).ptype)) == -1) \
    { \
	int save_errno = errno; \
        nexus_fatal("MP_SEND(): _isend() failed, errno=%d, %s\n", save_errno, _nx_md_system_error_string(save_errno)); \
    } \
}


/*
 * MP_SEND_STATUS(mp_send_status_t Status, nexus_bool_t Done)
 *
 * See if the send described by Status has completed.
 * If so, set Done=NEXUS_TRUE, else set Done=NEXUS_FALSE.
 */
#define MP_SEND_STATUS(Status, Done) \
{ \
    long inx_rc; \
    if ((inx_rc = _msgdone(Status)) == 1) \
    { \
	(Done) = NEXUS_TRUE; \
    } \
    else if (inx_rc == 0) \
    { \
	(Done) = NEXUS_FALSE; \
    } \
    else \
    { \
	int save_errno = errno; \
	nexus_fatal("MP_SEND_STATUS(): _msgdone() failed, errno=%d, %s\n", save_errno, _nx_md_system_error_string(save_errno)); \
    } \
}


/*
 * MP_COMPARE_DESTINATIONS(mp_destination_t D1, mp_destination_t D2,
 *			   nexus_bool_t Result)
 *
 * Set Result==NEXUS_TRUE if mp_destination_t's D1 and D2 are the same.
 */
#define MP_COMPARE_DESTINATIONS(D1, D2, Result) \
    if (   ((D1).node == (D2).node) \
	&& ((D1).ptype == (D2).ptype) ) \
        Result = NEXUS_TRUE; \
    else \
        Result = NEXUS_FALSE;


/*
 * MP_COPY_DESTINATION(mp_destination_t To, mp_destination_t From)
 *
 * Copy mp_destination_t 'From' to 'To'.
 */
#define MP_COPY_DESTINATION(To, From) \
{ \
    (To).node = (From).node; \
    (To).ptype = (From).ptype; \
}


/*
 * MP_FREE_DESTINATION(mp_destination_t Dest)
 *
 * Free up any memory in 'Dest' which was malloced.
 */
#define MP_FREE_DESTINATION(Dest)


/*
 * MP_HASH_DESTINATION(mp_destination_t Dest, int Hash)
 *
 * Hash the destination information into a value
 * between 0 and PROTO_TABLE_SIZE.

 * What about collisions? will need to look at ptype -- tal 7/15/94
 */
#define MP_HASH_DESTINATION(Dest, Hash) \
    (Hash) = (((Dest).node + (Dest).ptype) % PROTO_TABLE_SIZE)


/*
 * MP_INIT_DESTINATION(mp_destination_t Dest)
 *
 * Initialize the destination.
 */
#define MP_INIT_DESTINATION(Dest) \
{ \
    (Dest).node =  -1; \
    (Dest).ptype=  -1; \
}


/*
 * MP_NODE_EXIT()
 *
 * If a terminating process needs to do something to map itself out
 * of the set of processes, this macro should do it.
 */
#define MP_NODE_EXIT()


/*
 * MP_ABORT()
 *
 * Special statements to abort all processes.
 */
#define MP_ABORT() \
{ \
    /* Kill everything */ \
    kill (0, SIGKILL); \
}


/*
 * MP_GET_MY_MI_PROTO_SIZE(int Size)
 *
 * Fillin Size with the number of bytes I need to store my_node
 * into the mi_proto byte array.
 */
#define MP_GET_MY_MI_PROTO_SIZE(Size) (Size) = (sizeof(long) * 2)


/*
 * MP_GET_MY_MI_PROTO(nexus_byte_t *Array)
 *
 * Fillin my_node into the mi_proto byte array
 */
#define MP_GET_MY_MI_PROTO(Array) \
{ \
    memcpy((Array), &(my_node.node), sizeof(long)); \
    memcpy((Array) + sizeof(long), &(my_node.ptype), sizeof(long)); \
}


/*
 * MP_CONSTRUCT_FROM_MI_PROTO(mp_destination_t Dest,
 *			      nexus_mi_proto_t *Mi_proto,
 *			      nexus_byte_t *   Array)
 *
 * Copy the needed elements from Array to Dest.
 */
#define MP_CONSTRUCT_FROM_MI_PROTO(Dest, Mi_proto, Array) \
{ \
    memcpy(&((Dest).node), (Array), sizeof(long)); \
    memcpy(&((Dest).ptype), (Array) + sizeof(long), sizeof(long)); \
}


/*
 * MP_BROADCAST_COMMAND(int Command)
 *
 * Send the command out to everybody.
 *
 * NOTE: This is hacked!!! 7/25/94 tal
 * Will need some way of broadcasting to all known ptypes.
 */
#define MP_BROADCAST_COMMAND(Command) \
{ \
    nexus_byte_t inx_buf[1]; \
    inx_buf[0] = (nexus_byte_t) (Command); \
    if (_csend (INX_MSG_TYPE, (char *) inx_buf, 1, -1, 0) == -1) \
    { \
	int save_errno = errno; \
	nexus_fatal("MP_BROADCAST_COMMAND(): _csend() error, errno=%d, %s\n", save_errno, _nx_md_system_error_string(save_errno)); \
    } \
}

extern void _nx_clean_quit (int);

/*
 * PARAGON_SHUTDOWN
 * Macro to terminate all nodes.  This should only be called in
 * mp_shutdown().
 */
#define PARAGON_SHUTDOWN \
{ \
	nexus_debug_printf (2, ("shutdown_others node %d.\n", mynode())); \
	if (nexus_master_node()) { \
		int mask; \
		mask = sigmask (SIGUSR1); \
		sigblock (mask); \
		(*signal(SIGUSR1, _nx_clean_quit)) (SIGUSR1); \
		kill (0, SIGUSR1); \
	} \
}

#else   /* not HAVE_INX_PROTO */

#define GLOBUS_NEXUS_MP_PROTOCOL_INFO      globus_i_nexus_pr_inx_info
#define GLOBUS_NEXUS_MP_PROTOCOL_TYPE      globus_i_nexus_pr_inx_type
#define GLOBUS_NEXUS_MP_PROTOCOL_COUNT     globus_i_nexus_pr_inx_count
#define GLOBUS_NEXUS_MP_PROTOCOL_SP_MATCH  globus_i_nexus_pr_inx_sp_match

#define GLOBUS_NEXUS_PROTO_TYPE_MP NEXUS_PROTO_TYPE_INX

#endif /* HAVE_INX_PROTO */

EXTERN_C_END

#endif
