📄 rvselect.c
字号:
status = RvSocketBind(&selectEngine->preemptionSocket, &selectEngine->localAddress, NULL);
if (status != RV_OK)
{
RvSocketDestruct(&selectEngine->preemptionSocket, RV_TRUE, NULL);
return status;
}
status = RvSocketGetLocalAddress(&selectEngine->preemptionSocket, &selectEngine->localAddress);
if (status != RV_OK)
{
RvSocketDestruct(&selectEngine->preemptionSocket, RV_TRUE, NULL);
return status;
}
status = RvFdConstruct(&selectEngine->preemptionFd, &selectEngine->preemptionSocket);
}
#elif (RV_SELECT_PREEPEMTION_TYPE == RV_SOCKET_PREEMPTION_PIPE)
/* Create the preemption pipe if needed */
if (pipe(selectEngine->preemptionPipeFd) != 0)
{
RvSelectLogError((&rvSelectLogSource,
"RvSelectConstruct: Error creating pipe (%d:%s)",
RvSelectStrError(RvSelectErrNo), RvSelectErrNo));
return RvSelectErrorCode(RV_SELECT_ERROR_PIPE);
}
else
{
/* Set the pipe's file descriptors to non-blocking */
if (fcntl(selectEngine->preemptionPipeFd[0], F_SETFL, O_NONBLOCK) ||
fcntl(selectEngine->preemptionPipeFd[1], F_SETFL, O_NONBLOCK))
{
RvSelectLogError((&rvSelectLogSource,
"RvSelectConstruct: Error calling fcntl on pipe(%d:%s)",
RvSelectStrError(RvSelectErrNo), RvSelectErrNo));
close(selectEngine->preemptionPipeFd[0]);
close(selectEngine->preemptionPipeFd[1]);
return RvSelectErrorCode(RV_SELECT_ERROR_PIPE);
}
}
status = RvFdConstruct(&selectEngine->preemptionFd, &selectEngine->preemptionPipeFd[0]);
#endif
if (status == RV_OK)
{
/* Make sure we're waiting for READ events on this preemption fd */
status = RvSelectAdd(selectEngine, &selectEngine->preemptionFd, RV_SELECT_READ, rvFdPreemptionCallback);
}
if (status != RV_OK)
{
RvFdDestruct(&selectEngine->preemptionFd);
#if (RV_SELECT_PREEPEMTION_TYPE == RV_SOCKET_PREEMPTION_SOCKET)
RvSocketDestruct(&selectEngine->preemptionSocket, RV_FALSE, NULL);
#elif (RV_SELECT_PREEPEMTION_TYPE == RV_SOCKET_PREEMPTION_PIPE)
close(selectEngine->preemptionPipeFd[0]);
close(selectEngine->preemptionPipeFd[1]);
#endif
}
return status;
}
/********************************************************************************************
* rvFdPreemptionDestruct
* purpose : Destruct a preemption fd to use when we want to stop a select() loop from
* blocking.
* input : selectEngine - Events engine to use
* output : none
* return : RV_OK on success, other on failure
********************************************************************************************/
static RvStatus rvFdPreemptionDestruct(
IN RvSelectEngine *selectEngine)
{
RvFdDestruct(&selectEngine->preemptionFd);
#if (RV_SELECT_PREEPEMTION_TYPE == RV_SOCKET_PREEMPTION_SOCKET)
RvSocketDestruct(&selectEngine->preemptionSocket, RV_FALSE, NULL);
RvAddressDestruct(&selectEngine->localAddress);
#elif (RV_SELECT_PREEPEMTION_TYPE == RV_SOCKET_PREEMPTION_PIPE)
close(selectEngine->preemptionPipeFd[0]);
close(selectEngine->preemptionPipeFd[1]);
#endif
return RV_OK;
}
/********************************************************************************************
* rvFdPreempt
* purpose : Make sure we're out of the select() loop.
* input : selectEngine - Events engine to use
* output : none
* return : RV_OK on success, other on failure
********************************************************************************************/
static RvStatus rvFdPreempt(
IN RvSelectEngine *selectEngine)
{
RvStatus status = RV_OK;
if (selectEngine->waiting)
{
/* We're waiting - let's preempt */
#if (RV_SELECT_PREEPEMTION_TYPE == RV_SOCKET_PREEMPTION_SOCKET)
status = RvSocketSendBuffer(&selectEngine->preemptionSocket, (RvUint8*)" ", 1, &selectEngine->localAddress, NULL);
#elif (RV_SELECT_PREEPEMTION_TYPE == RV_SOCKET_PREEMPTION_PIPE)
if (write(selectEngine->preemptionPipeFd[1], " ", 1) < 0)
{
RvSelectLogError((&rvSelectLogSource,
"rvFdPreempt: Error calling write on pipe(%d:%s)",
RvSelectStrError(RvSelectErrNo), RvSelectErrNo));
status = RvSelectErrorCode(RV_SELECT_ERROR_PIPE);
}
#endif
}
return status;
}
#else
/* No need for preemption functions - we leave them empty */
#define rvFdPreemptionConstruct(_selectEngine) RV_OK
#define rvFdPreemptionDestruct(_selectEngine) RV_OK
#define rvFdPreempt(_selectEngine) RV_OK
#endif /* preemption functions */
#if (RV_SELECT_TYPE == RV_SELECT_WIN32_WSA)
/********************************************************************************************
*
* Windows (WSA) Specific internal functions
*
********************************************************************************************/
#define RV_FD_EVENTS_CLASS "RVSELECT"
/********************************************************************************************
* Window message types for message windows
*
* The actual values of these messages is not important and can be changed if they interfere
* with other messages in the application.
********************************************************************************************/
#define FD_NET_MESSAGE (WM_USER+100) /* Network event occured */
#define FD_NET_LOOP_END (WM_USER+101) /* Stop looping when trying to clear the queue */
#define FD_EXIT_MESSAGE (WM_USER+102) /* Indication to exit the message loop */
/********************************************************************************************
* rvSelectWinFunc
* purpose : Network window message handler
* This is the routine that is called by Windows and an event occur.
* It checks the nature of the event and act upon it.
* input : hWnd - Window handle
* uMsg - Window message to handle
* output : none
* return : Success
********************************************************************************************/
static LRESULT CALLBACK rvSelectWinFunc(HWND hWnd, UINT uMsg,WPARAM wParam,LPARAM lParam)
{
/* analize the events */
switch (uMsg)
{
/* Window close event - kill the window */
case WM_CLOSE:
{
DestroyWindow(hWnd);
break;
}
/* Someone asked to exit the message loop */
case FD_EXIT_MESSAGE:
{
RvSelectEngine* selectEngine;
#ifdef GetWindowLongPtr
/* 64bit support in Windows */
selectEngine = (RvSelectEngine *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
selectEngine = (RvSelectEngine *)GetWindowLong(hWnd, GWL_USERDATA);
#endif
/* Indicate we're done - the message loop checks this one */
selectEngine->exitMessageLoop = RV_TRUE;
break;
}
/* Network event received - send callback about it */
case FD_NET_MESSAGE:
{
RvSelectEngine* selectEngine;
RvSelectFd* fd;
#ifdef GetWindowLongPtr
/* 64bit support in Windows */
selectEngine = (RvSelectEngine *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
selectEngine = (RvSelectEngine *)GetWindowLong(hWnd, GWL_USERDATA);
#endif
/* Find the fd struct for this socket through the use of the bucket hash */
fd = fdBucketHashFind(selectEngine, (RvSocket)wParam);
/* Make sure we're waiting for events on this fd */
if (fd != NULL)
{
RvSelectCb cb;
RvSelectEvents events, originalEvents = (RvSelectEvents)0;
RvBool error, enableRead = RV_FALSE;
cb = fd->callback;
events = (RvSelectEvents)WSAGETSELECTEVENT(lParam);
error = (RvBool)WSAGETSELECTERROR(lParam);
/* Let's make sure we're actually waiting for this event */
if ((fd->events & events) != 0)
{
if ((events == RvSelectRead) && ((fd->events & RvSelectClose) != 0))
{
/* We know it's a TCP connection if we're waiting for close events... */
/* On read events of TCP connections, we had better remove the read
events until we're done with the callback, since Windows might send
us some more read events if we're calling recv() more than once from
the callback */
WSAAsyncSelect(fd->fd, hWnd, FD_NET_MESSAGE, fd->events & ~RvSelectRead);
enableRead = RV_TRUE;
/* Save the original events on this callback - they might be changed from
within the callback, and then, adding back the read event won't be wise */
originalEvents = fd->events;
}
RvSelectLogDebug((&rvSelectLogSource,
"Occured event: fd=%d,event=%s,error=%d", fd->fd, fdGetEventStr(events), error));
if (cb != NULL)
cb(selectEngine, fd, events, error);
if (enableRead && (originalEvents == fd->events))
{
/* Now we had better put back the missing read event, since we want it
back so badly and we're still waiting for it */
WSAAsyncSelect(fd->fd, hWnd, FD_NET_MESSAGE, fd->events | RvSelectRead);
}
}
}
break;
}
case FD_NET_LOOP_END:
/* Nothing to do really... */
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0l;
}
#endif /* RV_SELECT_TYPE == RV_SELECT_WIN32_WSA */
#if ((RV_SELECT_TYPE == RV_SELECT_SELECT) || (RV_SELECT_TYPE == RV_SELECT_POLL) || \
(RV_SELECT_TYPE == RV_SELECT_DEVPOLL))
/********************************************************************************************
*
* Non-Windows events translation
*
********************************************************************************************/
/* This macro translates events into read/write events so they can work for Unix style events */
#define rvSelectToOS(events) \
((( (events) & (RV_SELECT_READ | RV_SELECT_ACCEPT | RV_SELECT_CLOSE) ) != 0) ? \
( ( (events) & (RV_SELECT_WRITE | RV_SELECT_CONNECT) ) ? \
(RV_SELECT_READ | RV_SELECT_WRITE) : RV_SELECT_READ ) : \
RV_SELECT_WRITE)
/********************************************************************************************
* RvSelectEventsToUser
* purpose : Convert events that occured in the network to those asked by the user.
* It is needed when we're using Unix systems, that only handle read/write events.
* input : osFd - fd in the OS
* wantedEvents - Events the user asked for
* networkEvents - Events that occured on the network
* error - RV_TRUE if there was any error until now
* output : networkEvents - Events that occured on the network, translated to user events
* error - RV_TRUE if there was an error on the network side
* return : RV_OK on success, other on failure
********************************************************************************************/
static RvStatus RvSelectEventsToUser(
IN RvSocket* osFd,
IN RvSelectEvents wantedEvents,
INOUT RvSelectEvents* networkEvents,
INOUT RvBool* error)
{
RvSelectEvents translatedEvents = 0;
if ((*networkEvents) & RV_SELECT_WRITE)
{
/* First see about network write events */
if (wantedEvents & RV_SELECT_CONNECT)
translatedEvents = RV_SELECT_CONNECT;
else if (wantedEvents & RV_SELECT_WRITE)
translatedEvents = RV_SELECT_WRITE;
}
else if ((*networkEvents) & RV_SELECT_READ)
{
/* See what to do with read events - a little bit more tricky here */
if (wantedEvents & RV_SELECT_ACCEPT)
translatedEvents = RV_SELECT_ACCEPT;
else
{
if (wantedEvents & RV_SELECT_CLOSE)
{
/* Get the amount of bytes pending - this will indicate if the read event
occured because we're about to close this socket */
RvSize_t bytes;
RvStatus ret;
ret = RvSocketGetBytesAvailable(osFd, &bytes);
if (((ret == RV_OK) && (bytes == 0)) ||
((ret != RV_OK) && (RvErrorGetCode(ret) != RV_ERROR_NOTSUPPORTED)))
{
/* Error getting bytes on this socket */
translatedEvents = RV_SELECT_CLOSE;
}
else
{
RvAddress remoteAddress;
/* Getting the amount of bytes is not available on this platform or we
just got a good result... */
if (ret != RV_OK)
{
/* Handle not-supported errors as if we got 0 bytes */
bytes = 0;
}
/* Make sure we've got a remote address to work with, otherwise - the
connection closed */
ret = RvSocketGetRemoteAddress(osFd, &remoteAddress);
if (ret == RV_OK)
{
RvAddressDestruct(&remoteAddress);
}
else
{
/* Can't get remote address? We're closed for the day. */
translatedEvents = RV_SELECT_CLOSE;
}
}
}
/* It's not a close event and we're looking for read events */
if ((translatedEvents == 0) && (wantedEvents & RV_SELECT_READ))
translatedEvents = RV_SELECT_READ;
}
}
*networkEvents = translatedEvents;
/* See if we had an error on this socket */
if (*error == RV_FALSE)
{
RvInt32 lastError;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -