seli.c

来自「基于h323协议的软phone」· C语言 代码 · 共 594 行 · 第 1/2 页

C
594
字号
/***********************************************************************
Copyright (c) 2002 RADVISION Ltd.
************************************************************************
NOTICE:
This document contains information that is confidential and proprietary
to RADVISION Ltd.. No part of this document may be reproduced in any
form whatsoever without written prior approval by RADVISION Ltd..
RADVISION Ltd. reserve the right to revise this publication and make
changes without obligation to notify any person of such revisions or
changes.
***********************************************************************/

/*-----------------------------------------------------------------------*/
/*                        INCLUDE HEADER FILES                           */
/*-----------------------------------------------------------------------*/

#include "rvselect.h"
#include "rvtimer.h"
#include "ra.h"
#include "cm.h"
#include "rvthreadinstance.h"
#include "seli.h"


#ifdef __cplusplus
extern "C" {
#endif

/*-----------------------------------------------------------------------*/
/*                           TYPE DEFINITIONS                            */
/*-----------------------------------------------------------------------*/

#if (RV_OS_TYPE == RV_OS_TYPE_WIN32)
/* Our Windows implementation doesn't support seliCallOn() at all. */
#define SELI_MAX_USER_FDS (0)

#else
/* (RV_OS_TYPE != RV_OS_TYPE_WIN32) */
/* Maximum number of user FDs that will be used.
   This number is the amount of seliCallOn() calls on concurrent fds the user
   is about to use. */
#define SELI_MAX_USER_FDS (10)
#endif


/* User defined SELI events on file descriptors */
typedef struct
{
    RvSelectFd      fd; /* Actual core fd to use */
    seliCallback    callback; /* Callback to use */
} SeliUserFd;



/*-----------------------------------------------------------------------*/
/*                           MODULE VARIABLES                            */
/*-----------------------------------------------------------------------*/

/* Instance index used for the select engine */
static RvUint32 seliInstanceIndex = RV_UINT32_MAX;

/* Instance index used for the timer pool */
static RvUint32 timerQueueInstanceIndex = RV_UINT32_MAX;

/* Maximum number of user FDs that will be used.
   This number is the amount of seliCallOn() calls on concurrent fds the user
   is about to use. */
static RvSize_t seliMaxUserFds = SELI_MAX_USER_FDS;

/* Array holding the fd's memory allocations */
static HRA seliUserFds = NULL;

/* Indication how many times seliInit() was called. */
static RvInt seliInitCount = 0;




/*-----------------------------------------------------------------------*/
/*                        STATIC FUNCTIONS PROTOTYPES                    */
/*-----------------------------------------------------------------------*/

static RvStatus CreateSelectEngine(
    IN    const RvChar*             objectName,
    IN    void*                     context,
    IN    RvSize_t                  amountNeeded,
    INOUT void**                    objectPtr,
    OUT   RvSize_t*                 amountAllocated,
    OUT   ThreadInstanceDeleteFunc* deleteFunc);

static RvStatus DeleteSelectEngine(
    IN  const RvChar*   objectName,
    IN  void*           context,
    IN  void*           objectPtr);

static void seliEventsCB(
        IN RvSelectEngine*  selectEngine,
        IN RvSelectFd*      fd,
        IN RvSelectEvents   selectEvent,
        IN RvBool           error);






/*-----------------------------------------------------------------------*/
/*                           MODULE FUNCTIONS                            */
/*-----------------------------------------------------------------------*/


/************************************************************************
 * seliInit
 * purpose: Initialize a SELI interface.
 * input  : none
 * output : none
 * return : RV_OK on success, negative value on failure
 ************************************************************************/
RVAPI RvStatus RVCALLCONV seliInit(void)
{
    RvStatus status;

    cmStartUp();

    status = ThreadInstanceInitObject(RV_THREAD_INSTANCE_SELECT, CreateSelectEngine, NULL, 0,
        NULL, NULL, &seliInstanceIndex);

    /* Make sure we allocate some additional fd's for the user */
    if ((status == RV_OK) && (seliUserFds == NULL) && (seliMaxUserFds > 0) && (seliInitCount == 0))
    {
        seliUserFds = raConstruct(sizeof(SeliUserFd), seliMaxUserFds, RV_TRUE, "SELI USER FDs");
    }

    if (status == RV_OK)
        seliInitCount++;

    return status;
}

/************************************************************************
 * seliEnd
 * purpose: End a SELI interface.
 * input  : none
 * output : none
 * return : RV_OK on success, negative value on failure
 ************************************************************************/
RVAPI RvStatus RVCALLCONV seliEnd(void)
{
    RvStatus status;

    status = ThreadInstanceEndObject(seliInstanceIndex, 0, NULL);

    seliInitCount--;

    if ((seliUserFds != NULL) && (seliInitCount == 0))
    {
        raDestruct(seliUserFds);
        seliUserFds = NULL;
    }

    cmShutdown();

    return status;
}

/************************************************************************
 * seliSelect
 * purpose: Block on the select() interface on some operating systems.
 *          Use parallel interfaces on other operating systems.
 *          An application should write a "while (1) seliSelect();"
 *          as its main loop.
 * input  : none
 * output : none
 * return : RV_OK on success, negative value on failure
 ************************************************************************/
RVAPI RvStatus RVCALLCONV seliSelect(void)
{
    RvSelectEngine* selectEngine;
    RvTimerQueue* timerQueue = NULL;
    RvStatus status;
    RvUint64 timeout = RV_UINT64_MAX;

    status = ThreadInstanceGetObject(seliInstanceIndex, (void**)&selectEngine);
    if (status != RV_OK)
        return status;

    if (timerQueueInstanceIndex == RV_UINT32_MAX)
    {
        /* We need to find out the timer queue */
        status = ThreadInstanceFindIndex(RV_THREAD_INSTANCE_TIMERS, &timerQueueInstanceIndex);
    }

    if (timerQueueInstanceIndex != RV_UINT32_MAX)
    {
        RvInt64 nextEvent = 0;

        status = ThreadInstanceGetObject(timerQueueInstanceIndex, (void**)&timerQueue);
        if (status == RV_OK)
            status = RvTimerQueueNextEvent(timerQueue, &nextEvent);
        if (status == RV_OK)
        {
            if (nextEvent > 0)
                timeout = (RvUint64)nextEvent;
            else
                timeout = 0;
        }

        status = RV_OK; /* Make sure we select() */
    }

    if (status == RV_OK)
        status = RvSelectWaitAndBlock(selectEngine, timeout);

    /* Check for any timeout events - only do that if we didn't kill our seli right
       before this call*/
    if (ThreadInstanceGetObject(timerQueueInstanceIndex, (void**)&timerQueue) != RV_OK)
        timerQueue = NULL;
    if ((timerQueue != NULL) && (seliInitCount > 0))
        RvTimerQueueService(timerQueue, 0, NULL);

    return status;
}

/************************************************************************
 * seliCallOn
 * purpose: Ask the SELI interface for events on a given handle/file
 *          descriptor.
 * input  : fd              - Handle/File descriptor to wait on for events
 *          sEvents         - Events to wait for
 *          callbackFunc    - Function to call when the event occurs
 * output : none
 * return : RV_OK on success, negative value on failure
 ************************************************************************/
RVAPI RvStatus RVCALLCONV seliCallOn(
    IN int              fd,
    IN seliEvents       sEvents,
    IN seliCallback     callbackFunc)
{
    RvSelectEngine* selectEngine;
    RvStatus status;
    RvSocket s;
    RvSelectFd* coreFd;
    SeliUserFd* userFd;
    RvSelectEvents coreEvents = 0;

    /* Convert the events to the core's events */
    if (sEvents & seliEvRead) coreEvents |= RvSelectRead;
    if (sEvents & seliEvWrite) coreEvents |= RvSelectWrite;


    /* Find the select engine for this thread at first */
    status = ThreadInstanceGetObject(seliInstanceIndex, (void**)&selectEngine);
    if (status != RV_OK)
        return status;

    /* Look for this fd if we're currently waiting for events on it */
    s = (RvSocket)fd;
    coreFd = RvSelectFindFd(selectEngine, &s);

    if ((coreFd == NULL) && ((int)sEvents != 0) && (callbackFunc != NULL))
    {
        /* This is a new fd we're waiting on - add it */

        /* Allocate an FD for the user */
        if (seliUserFds == NULL)
            return RV_ERROR_NOTSUPPORTED;
        if (raAdd(seliUserFds, (RAElement*)&userFd) >= 0)
        {
            userFd->callback = callbackFunc;
            status = RvFdConstruct(&userFd->fd, &s);
            if (status == RV_OK)
            {
                status = RvSelectAdd(selectEngine, &userFd->fd, coreEvents, seliEventsCB);
                if (status != RV_OK) RvFdDestruct(&userFd->fd);
            }
            if (status != RV_OK)
                raDelete(seliUserFds, (RAElement)userFd);
        }
        else
            return RV_ERROR_OUTOFRESOURCES; /* No more fd's to spare */
    }
    else if (coreFd != NULL)
    {
        userFd = RV_GET_STRUCT(SeliUserFd, fd, coreFd);

        /* We already have it */
        if (((int)sEvents == 0) || (callbackFunc == NULL))
        {
            /* We should remove this fd */
            RvSelectRemove(selectEngine, &userFd->fd);
            RvFdDestruct(&userFd->fd);
            raDelete(seliUserFds, (RAElement)userFd);
        }
        else
        {
            /* We should update this fd */
            status = RvSelectUpdate(selectEngine, &userFd->fd, coreEvents, seliEventsCB);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?