select.c
来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 508 行
C
508 行
/*
*
* BSD sockets functionality for Waterloo TCP/IP
*
* Version
*
* 0.5 : Dec 18, 1997 : G. Vanem - created
* 0.6 : Nov 05, 1999 : G. Vanem - several changes;
* Protect select-loop as critical region.
* Changed criteria for read/writeability.
*/
#include "socket.h"
#if defined(USE_BSD_FUNC)
#ifdef __HIGHC__ /* set warning for stack-usage */
#pragma stack_size_warn (9200) /* ~3*MAX_SOCKETS */
#endif
/*
* Setup for read/write/except_select()
*/
static __inline Socket *setup_select (int s)
{
Socket *socket = _socklist_find (s);
if (!socket)
{
if (_sock_dos_fd(s))
{
SOCK_DEBUGF ((NULL, ", ENOTSOCK (%d)", s));
SOCK_ERR (ENOTSOCK);
}
else
{
SOCK_DEBUGF ((NULL, ", EBADF (%d)", s));
SOCK_ERR (EBADF);
}
}
return (socket);
}
int select_s (int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)
{
fd_set oread [NUM_SOCK_FDSETS];
fd_set owrite [NUM_SOCK_FDSETS];
fd_set oexcept[NUM_SOCK_FDSETS];
struct timeval starttime, expiry, now;
volatile int num_fd = nfds;
volatile int ret_count = 0;
volatile int expired = 0;
int s;
#if defined(USE_DEBUG)
unsigned total_rd = 0;
unsigned total_wr = 0;
unsigned total_ex = 0;
#endif
if (num_fd > MAX_SOCKETS)
num_fd = MAX_SOCKETS;
SOCK_DEBUGF ((NULL, "\nselect: n=0-%d, %c%c%c", num_fd-1,
readfds ? 'r' : '-',
writefds ? 'w' : '-',
exceptfds ? 'x' : '-'));
/* num_fd == 0 is permitted. Often used to perform delays:
* select (0,NULL,NULL,NULL,&tv);
*/
if (num_fd < 0)
{
SOCK_DEBUGF ((NULL, ", (EINVAL), num_fd %d", num_fd));
SOCK_ERR (EINVAL);
return (-1);
}
if (timeout)
{
if ((long)timeout->tv_sec < 0 || timeout->tv_usec < 0)
{
SOCK_DEBUGF ((NULL, ", negative timeout (EINVAL)"));
SOCK_ERR (EINVAL);
return (-1);
}
gettimeofday2 (&starttime, NULL); /* initialize start time */
expiry.tv_sec = starttime.tv_sec + timeout->tv_sec;
expiry.tv_usec = starttime.tv_usec + timeout->tv_usec;
while (expiry.tv_usec >= 1000000UL)
{
expiry.tv_usec -= 1000000UL;
expiry.tv_sec++;
}
SOCK_DEBUGF ((NULL, ", timeout %u.%06lds",
timeout->tv_sec, timeout->tv_usec));
}
else
SOCK_DEBUGF ((NULL, ", timeout undef"));
/* Clear our "working" fd_sets
*/
FD_ZERO (&oread[0]);
FD_ZERO (&owrite[0]);
FD_ZERO (&oexcept[0]);
/* If application catches same signals we do, we must exit
* gracefully from the do-while and for loops below.
*/
if (_sock_sig_setup() < 0)
{
SOCK_ERR (EINTR);
goto select_fail;
}
/*
* Loop until specified timeout expires or event(s) satisfied.
*/
do
{
for (s = 0; s < num_fd; s++)
{
/* read/write/except counters for socket 's'
*/
int do_read = 0, do_write = 0, do_exc = 0;
int read_cnt = 0, write_cnt = 0, exc_cnt = 0;
Socket *socket = NULL;
#if (SK_FIRST > 0)
/* We ignore stdin/stdout/stderr handles for now
*/
if (s < SK_FIRST)
continue;
#endif
/* Not safe to run sock_daemon() (or other "tasks") now
*/
_sock_start_crit();
if (readfds && FD_ISSET(s,readfds))
do_read = 1;
if (writefds && FD_ISSET(s,writefds))
do_write = 1;
if (exceptfds && FD_ISSET(s,exceptfds))
do_exc = 1;
if (do_read || do_write || do_exc)
{
/* lookup 'socket' only once for each 's'
*/
socket = setup_select (s);
if (!socket)
goto select_fail;
tcp_tick (NULL);
}
/* Check this socket for readability ?
*/
if (do_read)
{
read_cnt = _sock_read_select (socket);
if (read_cnt > 0)
FD_SET (s, &oread[0]);
}
/* Check this socket for writeability ?
*/
if (do_write)
{
write_cnt = _sock_write_select (socket);
if (write_cnt > 0)
FD_SET (s, &owrite[0]);
}
/* Check this socket for exception ?
*/
if (do_exc)
{
exc_cnt = _sock_exc_select (socket);
if (exc_cnt > 0)
FD_SET (s, &oexcept[0]);
}
/* Increment the return and total counters (may incr. by 0)
*/
ret_count += (read_cnt + write_cnt + exc_cnt);
#if defined(USE_DEBUG)
total_rd += read_cnt;
total_wr += write_cnt;
total_ex += exc_cnt;
#endif
/* Safe to run other "tasks" now.
*/
_sock_stop_crit();
_sock_dbgflush();
SOCK_YIELD();
} /* end of for loop; all sockets checked at least once */
if (timeout)
{
gettimeofday2 (&now, NULL);
if (now.tv_sec > expiry.tv_sec ||
(now.tv_sec == expiry.tv_sec && now.tv_usec > expiry.tv_usec))
expired = TRUE;
}
/* At least 1 event is set.
*/
if (ret_count > 0)
{
SOCK_DEBUGF ((NULL, ", cnt=%d (%dr/%dw/%dx)",
ret_count, total_rd, total_wr, total_ex));
/* Copy our "working" fd_sets to output fd_sets
*/
for (s = 0; s < num_fd; s++)
{
if (readfds)
{
if (FD_ISSET(s, &oread[0]))
FD_SET (s, readfds);
else FD_CLR (s, readfds);
}
if (writefds)
{
if (FD_ISSET(s, &owrite[0]))
FD_SET (s, writefds);
else FD_CLR (s, writefds);
}
if (exceptfds)
{
if (FD_ISSET(s, &oexcept[0]))
FD_SET (s, exceptfds);
else FD_CLR (s, exceptfds);
}
}
/* Do as Linux and return the time left of the period.
* NB! The tv_sec might be negative if select_s() took too long.
*/
if (timeout)
{
double remaining = timeval_diff (&now, &expiry);
timeout->tv_sec = (long)(remaining / 1E6);
timeout->tv_usec = (long)remaining % 1000000UL;
}
goto select_ok;
}
if (expired)
{
SOCK_DEBUGF ((NULL, ", timeout!: %.6fs",
timeval_diff(&now, &starttime)/1E6));
for (s = 0; s < num_fd; s++)
{
if (readfds) FD_CLR (s, readfds);
if (writefds) FD_CLR (s, writefds);
if (exceptfds) FD_CLR (s, exceptfds);
}
ret_count = 0; /* should already be 0 */
goto select_ok;
}
}
while (1);
select_fail:
ret_count = -1;
_sock_stop_crit();
select_ok:
_sock_sig_restore();
return (ret_count);
}
/*
* Check listen-queue for first connected TCB.
* Only called for is listening (accepting) sockets.
*/
static __inline int listen_queued (Socket *socket)
{
int i;
for (i = 0; i < socket->backlog && i < DIM(socket->listen_queue); i++)
{
tcp_Socket *tcb = socket->listen_queue[i];
if (!tcb)
continue;
/* Socket has reached Established state or receive data above
* low water mark. This means, socket may have reached Closed,
* but this still counts as a readable event.
*/
if (tcb->state == tcp_StateESTAB ||
sock_rbused((sock_type*)tcb) > socket->recv_lowat)
return (1);
}
return (0);
}
/*
* Return TRUE if socket has a "real" error and not a "pending" error.
* I.e. EALREADY is not a real error, but a pending condition until a
* non-blocking socket is actually connected. Or if connection fails,
* in which case the error is ECONNREFUSED.
*
* This signalled read/write state is assumed to persist for the
* remaining life of the socket.
*/
#define READ_STATE_MASK (SS_CANTRCVMORE) /* set in recv() or shutdown() */
#define WRITE_STATE_MASK (SS_CANTSENDMORE|SS_ISCONNECTED)
static __inline int sock_signalled (Socket *socket, int mask)
{
if (socket->so_state & mask)
return (1);
/* A normal blocking socket. 'so_error' is set in
* connect(), a "ICMP Unreachable" callback or when RST received
* in pctcp.c. Otherwise 'so_error' is zero.
*/
if (!(socket->so_state & SS_NBIO))
return (socket->so_error);
#if 0
if (socket->so_options & SO_ACCEPTCONN) /* non-blocking listen sock */
return (0);
if (socket->so_error == EALREADY) /* temporary non-blocking error */
return (0);
return (1);
#else
return (0);
#endif
}
/*
* Check if socket can be read from.
*/
int _sock_read_select (Socket *socket)
{
int len;
#if defined(USE_LIBPCAP)
if (socket->so_type == SOCK_PACKET)
{
/* !!fix-me: need to push-back this packet, else it's lost
* when read_s()/recv() is called.
*/
if (pcap_peek(_pcap_w32))
return (1);
return (0);
}
#endif
if (socket->so_type == SOCK_RAW)
return (socket->raw_sock && socket->raw_sock->used);
if (socket->so_type == SOCK_DGRAM)
{
if (socket->so_state & SS_PRIV)
len = sock_recv_used (socket->udp_sock);
else len = sock_rbused ((sock_type*)socket->udp_sock);
if (len > socket->recv_lowat ||
sock_signalled(socket,READ_STATE_MASK))
return (1);
return (0);
}
if (socket->so_type == SOCK_STREAM)
{
sock_type *sk = (sock_type*) socket->tcp_sock;
if (sock_signalled(socket,READ_STATE_MASK) || /* signalled for read_s() */
sk->tcp.state >= tcp_StateLASTACK || /* got FIN from peer */
sock_rbused(sk) > socket->recv_lowat) /* Rx-data above low-limit */
return (1);
if ((socket->so_options & SO_ACCEPTCONN) && /* connection pending */
listen_queued(socket))
return (1);
return (0);
}
return (0);
}
/*
* Check if socket can be written to.
*/
int _sock_write_select (Socket *socket)
{
/* SOCK_PACKET, SOCK_RAW and SOCK_DGRAM sockets are always writeable
*/
#if defined(USE_LIBPCAP)
if (socket->so_type == SOCK_PACKET)
return (1);
#endif
if (socket->so_type == SOCK_RAW ||
socket->so_type == SOCK_DGRAM)
return (1);
if (socket->so_type == SOCK_STREAM)
{
sock_type *sk = (sock_type*) socket->tcp_sock;
if (sock_tbleft(sk) > socket->send_lowat || /* Tx room above low-limit */
sock_signalled(socket,WRITE_STATE_MASK)) /* signalled for write */
return (1);
return (0);
}
return (0); /* shouldn't happen */
}
/*
* Check socket for exception or faulty condition.
*/
int _sock_exc_select (Socket *socket)
{
#if defined(USE_LIBPCAP)
if (socket->so_type == SOCK_PACKET &&
pcap_geterr(_pcap_w32))
return (1);
#else
ARGSUSED (socket);
#endif
/* !!to-do: Only arrival of OOB-data should count here
*/
#if 0
if (sock_signalled(socket, READ_STATE_MASK|WRITE_STATE_MASK))
return (1);
#endif
return (0);
}
#endif /* USE_BSD_FUNC */
/*
* A small test program, for djgpp only
*/
#if defined(TEST_PROG)
#include <unistd.h>
#include "pcdbug.h"
int main (int argc, char **argv)
{
DWORD usec;
if (argc < 2)
{
fprintf (stderr, "Usage: %s micro-sec\n", argv[0]);
return (-1);
}
usec = atol (argv[1]);
dbug_init();
sock_init();
printf ("has_8254 %d\n", has_8254);
while (!kbhit())
{
struct timeval tv;
uclock_t start, diff;
tv.tv_sec = usec / 1000000UL;
tv.tv_usec = usec % 1000000UL;
start = uclock();
select_s (1, NULL, NULL, NULL, &tv); /* select in the "for loop" */
diff = uclock() - start;
SOCK_DEBUGF ((NULL, ", diff: %.3fs", (double)diff/(double)UCLOCKS_PER_SEC));
fputc ('.', stderr);
usleep (usec + 10000UL); /* >= 10 msec */
}
return (0);
}
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?