select.cc
来自「cygwin, 著名的在win32下模拟unix操作系统的东东」· CC 代码 · 共 1,496 行 · 第 1/3 页
CC
1,496 行
/* select.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. Written by Christopher Faylor of Cygnus Solutions cgf@cygnus.comThis file is part of Cygwin.This software is a copyrighted work licensed under the terms of theCygwin license. Please consult the file "CYGWIN_LICENSE" fordetails. *//* * The following line means that the BSD socket * definitions for fd_set, FD_ISSET etc. are used in this * file. */#define __INSIDE_CYGWIN_NET__#include "winsup.h"#include <errno.h>#include <sys/socket.h>#include <stdlib.h>#include <sys/time.h>#include <wingdi.h>#include <winuser.h>#include <netdb.h>#include <unistd.h>#include <stdio.h>#define USE_SYS_TYPES_FD_SET#include <winsock.h>#include "select.h"#include "cygerrno.h"#include "security.h"#include "fhandler.h"#include "path.h"#include "dtable.h"#include "cygheap.h"#include "sigproc.h"#include "perthread.h"#include "tty.h"#include "cygthread.h"/* * All these defines below should be in sys/types.h * but because of the includes above, they may not have * been included. We create special UNIX_xxxx versions here. */#ifndef NBBY#define NBBY 8 /* number of bits in a byte */#endif /* NBBY *//* * Select uses bit masks of file descriptors in longs. * These macros manipulate such bit fields (the filesystem macros use chars). * FD_SETSIZE may be defined by the user, but the default here * should be >= NOFILE (param.h). */typedef long fd_mask;#define UNIX_NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */#ifndef unix_howmany#define unix_howmany(x,y) (((x)+((y)-1))/(y))#endif#define unix_fd_set fd_set#define NULL_fd_set ((fd_set *) NULL)#define sizeof_fd_set(n) \ ((unsigned) (NULL_fd_set->fds_bits + unix_howmany ((n), UNIX_NFDBITS)))#define UNIX_FD_SET(n, p) \ ((p)->fds_bits[(n)/UNIX_NFDBITS] |= (1L << ((n) % UNIX_NFDBITS)))#define UNIX_FD_CLR(n, p) \ ((p)->fds_bits[(n)/UNIX_NFDBITS] &= ~(1L << ((n) % UNIX_NFDBITS)))#define UNIX_FD_ISSET(n, p) \ ((p)->fds_bits[(n)/UNIX_NFDBITS] & (1L << ((n) % UNIX_NFDBITS)))#define UNIX_FD_ZERO(p, n) \ bzero ((caddr_t)(p), sizeof_fd_set ((n)))#define allocfd_set(n) ((fd_set *) memset (alloca (sizeof_fd_set (n)), 0, sizeof_fd_set (n)))#define copyfd_set(to, from, n) memcpy (to, from, sizeof_fd_set (n));#define set_handle_or_return_if_not_open(h, s) \ h = (s)->fh->get_handle (); \ if (cygheap->fdtab.not_open ((s)->fd)) \ { \ (s)->saw_error = true; \ set_sig_errno (EBADF); \ return -1; \ } \/* The main select code. */extern "C"intcygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *to){ select_stuff sel; fd_set *dummy_readfds = allocfd_set (maxfds); fd_set *dummy_writefds = allocfd_set (maxfds); fd_set *dummy_exceptfds = allocfd_set (maxfds); sigframe thisframe (mainthread); select_printf ("%d, %p, %p, %p, %p", maxfds, readfds, writefds, exceptfds, to); if (!readfds) readfds = dummy_readfds; if (!writefds) writefds = dummy_writefds; if (!exceptfds) exceptfds = dummy_exceptfds; for (int i = 0; i < maxfds; i++) if (!sel.test_and_set (i, readfds, writefds, exceptfds)) { select_printf ("aborting due to test_and_set error"); return -1; /* Invalid fd, maybe? */ } /* Convert to milliseconds or INFINITE if to == NULL */ DWORD ms = to ? (to->tv_sec * 1000) + (to->tv_usec / 1000) : INFINITE; if (ms == 0 && to->tv_usec) ms = 1; /* At least 1 ms granularity */ if (to) select_printf ("to->tv_sec %d, to->tv_usec %d, ms %d", to->tv_sec, to->tv_usec, ms); else select_printf ("to NULL, ms %x", ms); select_printf ("sel.always_ready %d", sel.always_ready); int timeout = 0; /* Allocate some fd_set structures using the number of fds as a guide. */ fd_set *r = allocfd_set (maxfds); fd_set *w = allocfd_set (maxfds); fd_set *e = allocfd_set (maxfds); /* Degenerate case. No fds to wait for. Just wait. */ if (sel.start.next == NULL) { if (WaitForSingleObject (signal_arrived, ms) == WAIT_OBJECT_0) { select_printf ("signal received"); set_sig_errno (EINTR); return -1; } timeout = 1; } else if (sel.always_ready || ms == 0) /* Don't bother waiting. */; else if ((timeout = sel.wait (r, w, e, ms) < 0)) return -1; /* some kind of error */ sel.cleanup (); copyfd_set (readfds, r, maxfds); copyfd_set (writefds, w, maxfds); copyfd_set (exceptfds, e, maxfds); return timeout ? 0 : sel.poll (readfds, writefds, exceptfds);}/* Call cleanup functions for all inspected fds. Gets rid of any executing threads. */voidselect_stuff::cleanup (){ select_record *s = &start; select_printf ("calling cleanup routines"); while ((s = s->next)) if (s->cleanup) { s->cleanup (s, this); s->cleanup = NULL; }}/* Destroy all storage associated with select stuff. */select_stuff::~select_stuff (){ cleanup (); select_record *s = &start; select_record *snext = start.next; select_printf ("deleting select records"); while ((s = snext)) { snext = s->next; delete s; }}/* Add a record to the select chain */intselect_stuff::test_and_set (int i, fd_set *readfds, fd_set *writefds, fd_set *exceptfds){ select_record *s = NULL; if (UNIX_FD_ISSET (i, readfds) && (s = cygheap->fdtab.select_read (i, s)) == NULL) return 0; /* error */ if (UNIX_FD_ISSET (i, writefds) && (s = cygheap->fdtab.select_write (i, s)) == NULL) return 0; /* error */ if (UNIX_FD_ISSET (i, exceptfds) && (s = cygheap->fdtab.select_except (i, s)) == NULL) return 0; /* error */ if (s == NULL) return 1; /* nothing to do */ if (s->read_ready || s->write_ready || s->except_ready) always_ready = true; if (s->windows_handle || s->windows_handle || s->windows_handle) windows_used = true; s->next = start.next; start.next = s; return 1;}/* The heart of select. Waits for an fd to do something interesting. */intselect_stuff::wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds, DWORD ms){ int wait_ret; HANDLE w4[MAXIMUM_WAIT_OBJECTS]; select_record *s = &start; int m = 0; int res = 0; w4[m++] = signal_arrived; /* Always wait for the arrival of a signal. */ /* Loop through the select chain, starting up anything appropriate and counting the number of active fds. */ while ((s = s->next)) { if (m > MAXIMUM_WAIT_OBJECTS) { set_sig_errno (EINVAL); return -1; } if (!s->startup (s, this)) { __seterrno (); return -1; } if (s->h == NULL) continue; for (int i = 1; i < m; i++) if (w4[i] == s->h) goto next_while; w4[m++] = s->h; next_while: continue; } DWORD start_time = GetTickCount (); /* Record the current time for later use. */ debug_printf ("m %d, ms %u", m, ms); for (;;) { if (!windows_used) wait_ret = WaitForMultipleObjects (m, w4, FALSE, ms); else wait_ret = MsgWaitForMultipleObjects (m, w4, FALSE, ms, QS_ALLINPUT); switch (wait_ret) { case WAIT_OBJECT_0: select_printf ("signal received"); set_sig_errno (EINTR); return -1; case WAIT_FAILED: select_printf ("WaitForMultipleObjects failed"); __seterrno (); return -1; case WAIT_TIMEOUT: select_printf ("timed out"); res = 1; goto out; } select_printf ("woke up. wait_ret %d. verifying", wait_ret); s = &start; int gotone = FALSE; /* Some types of object (e.g., consoles) wake up on "inappropriate" events like mouse movements. The verify function will detect these situations. If it returns false, then this wakeup was a false alarm and we should go back to waiting. */ while ((s = s->next)) if (s->saw_error) return -1; /* Somebody detected an error */ else if ((((wait_ret >= m && s->windows_handle) || s->h == w4[wait_ret])) && s->verify (s, readfds, writefds, exceptfds)) gotone = true; select_printf ("gotone %d", gotone); if (gotone) goto out; if (ms == INFINITE) { select_printf ("looping"); continue; } select_printf ("recalculating ms"); DWORD now = GetTickCount (); if (now > (start_time + ms)) { select_printf ("timed out after verification"); goto out; } ms -= (now - start_time); start_time = now; select_printf ("ms now %u", ms); }out: select_printf ("returning %d", res); return res;}static intset_bits (select_record *me, fd_set *readfds, fd_set *writefds, fd_set *exceptfds){ int ready = 0; select_printf ("me %p, testing fd %d (%s)", me, me->fd, me->fh->get_name ()); if (me->read_selected && me->read_ready) { UNIX_FD_SET (me->fd, readfds); ready++; } if (me->write_selected && me->write_ready) { UNIX_FD_SET (me->fd, writefds); if (me->except_on_write && me->fh->get_device () == FH_SOCKET) ((fhandler_socket *) me->fh)->set_connect_state (CONNECTED); ready++; } if ((me->except_selected || me->except_on_write) && me->except_ready) { if (me->except_on_write) /* Only on sockets */ { UNIX_FD_SET (me->fd, writefds); if (me->fh->get_device () == FH_SOCKET) ((fhandler_socket *) me->fh)->set_connect_state (CONNECTED); } if (me->except_selected) UNIX_FD_SET (me->fd, exceptfds); ready++; } select_printf ("ready %d", ready); return ready;}/* Poll every fd in the select chain. Set appropriate fd in mask. */intselect_stuff::poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds){ int n = 0; select_record *s = &start; while ((s = s->next)) n += (!s->peek || s->peek (s, true)) ? set_bits (s, readfds, writefds, exceptfds) : 0; select_printf ("returning %d", n); return n;}static intverify_true (select_record *, fd_set *, fd_set *, fd_set *){ return 1;}static intverify_ok (select_record *me, fd_set *readfds, fd_set *writefds, fd_set *exceptfds){ return set_bits (me, readfds, writefds, exceptfds);}static intno_startup (select_record *, select_stuff *){ return 1;}static intno_verify (select_record *, fd_set *, fd_set *, fd_set *){ return 0;}static intpeek_pipe (select_record *s, bool from_select){ int n = 0; int gotone = 0; fhandler_base *fh = s->fh; HANDLE h; set_handle_or_return_if_not_open (h, s); /* pipes require a guard mutex to guard against the situation where multiple readers are attempting to read from the same pipe. In this scenario, it is possible for PeekNamedPipe to report available data to two readers but only one will actually get the data. This will result in the other reader entering fhandler_base::raw_read and blocking indefinitely in an interruptible state. This causes things like "make -j2" to hang. So, for the non-select case we use the pipe mutex, if it is available. */ HANDLE guard_mutex = from_select ? NULL : fh->get_guard (); /* Don't perform complicated tests if we don't need to. */ if (!s->read_selected && !s->except_selected) goto out; if (s->read_selected) { if (s->read_ready) { select_printf ("already ready"); gotone = 1; goto out; } switch (fh->get_device ()) { case FH_PTYM: case FH_TTYM: if (((fhandler_pty_master *)fh)->need_nl) { gotone = s->read_ready = true; goto out; } break; default: if (fh->get_readahead_valid ()) { select_printf ("readahead"); gotone = s->read_ready = true; goto out; } } if (fh->bg_check (SIGTTIN) <= bg_eof) { gotone = s->read_ready = true; goto out; } } if (fh->get_device () == FH_PIPEW) /* nothing */; else if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL)) { select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ()); n = -1; } else if (!n || !guard_mutex) /* no guard mutex or nothing to read from the pipe. */; else if (WaitForSingleObject (guard_mutex, 0) != WAIT_OBJECT_0) { select_printf ("%s, couldn't get mutex %p, %E", fh->get_name (), guard_mutex); n = 0; } else { /* Now that we have the mutex, make sure that no one else has snuck in and grabbed the data that we originally saw. */ if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL)) { select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ()); n = -1; } if (n <= 0) ReleaseMutex (guard_mutex); /* Oops. We lost the race. */ } if (n < 0) { fh->set_eof (); /* Flag that other end of pipe is gone */ select_printf ("%s, n %d", fh->get_name (), n); if (s->except_selected) gotone += s->except_ready = true; if (s->read_selected) gotone += s->read_ready = true; } if (n > 0 && s->read_selected) { select_printf ("%s, ready for read", fh->get_name ()); gotone += s->read_ready = true; } if (!gotone && s->fh->hit_eof ()) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?