/*
****************************************************************************
*        Copyright IBM Corporation 1988, 1989 - All Rights Reserved        *
*                                                                          *
* Permission to use, copy, modify, and distribute this software and its    *
* documentation for any purpose and without fee is hereby granted,         *
* provided that the above copyright notice appear in all copies and        *
* that both that copyright notice and this permission notice appear in     *
* supporting documentation, and that the name of IBM not be used in        *
* advertising or publicity pertaining to distribution of the software      *
* without specific, written prior permission.                              *
*                                                                          *
* IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL *
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM *
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY      *
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER  *
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING   *
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.    *
****************************************************************************
*/

#include "rx_locl.h"

RCSID("$Id: rx_multi.c,v 1.6 2000/03/03 08:56:12 assar Exp $");

/* multi.c and multi.h, together with some rxgen hooks, provide a way of making multiple, but similar, rx calls to multiple hosts simultaneously */

struct multi_handle *
multi_Init(struct rx_connection **conns, int nConns)
{
    struct rx_call **calls;
    short *ready;
    struct multi_handle *mh;
    int i;

    /*
     * Note:  all structures that are possibly referenced by other processes
     * must be allocated.  In some kernels variables allocated on a process
     * stack will not be accessible to other processes
     */
    calls = (struct rx_call **) osi_Alloc(sizeof(struct rx_call *) * nConns);
    ready = (short *) osi_Alloc(sizeof(short *) * nConns);
    mh = (struct multi_handle *) osi_Alloc(sizeof(struct multi_handle));
    if (!calls || !ready || !mh)
	osi_Panic("multi_Rx: no mem\n");
    mh->calls = calls;
    mh->nextReady = mh->firstNotReady = mh->ready = ready;
    mh->nReady = 0;
    mh->nConns = nConns;
    for (i = 0; i < nConns; i++) {
	struct rx_call *call;

	call = mh->calls[i] = rx_NewCall(conns[i]);
	rx_SetArrivalProc(call, multi_Ready, (void *) mh, (void *) i);
    }
    return mh;
}

/*
 * Return the user's connection index of the most recently ready call; that
 * is, a call that has received at least one reply packet 
 */
int 
multi_Select(struct multi_handle *mh)
{
    SPLVAR;
    NETPRI;
    if (mh->nextReady == mh->firstNotReady) {
	if (mh->nReady == mh->nConns) {
	    USERPRI;
	    return -1;
	}
	osi_rxSleep(mh);
    }
    USERPRI;
    return *mh->nextReady++;
}

/*
 * Called by Rx when the first reply packet of a call is received, or the
 * call is aborted. 
 */
void 
multi_Ready(struct rx_call *call, struct multi_handle *mh,
	    int index)
{
    *mh->firstNotReady++ = index;
    mh->nReady++;
    osi_rxWakeup(mh);
}

/*
 * Called when the multi rx call is over, or when the user aborts it 
 * (by using the macro multi_Abort)
 */
void 
multi_Finalize(struct multi_handle *mh)
{
    int i;
    int nCalls = mh->nConns;

    for (i = 0; i < nCalls; i++) {
	struct rx_call *call = mh->calls[i];

	if (call)
	    rx_EndCall(call, RX_USER_ABORT);
    }
    osi_Free(mh->calls, sizeof(struct rx_call *) * nCalls);
    osi_Free(mh->ready, sizeof(short *) * nCalls);
}
