From 3222f6ee191c0410a5676fdb3ba2fec093dbca32 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Tue, 25 Jul 2006 17:02:33 +0000 Subject: * cygtls.h: Drop socket related includes. (struct _local_storage): Remove exitsock and exitsock_sin. Add select_sockevt. * cygtls.cc: Accomodate above change throughout. * fhandler.h (class fhandler_socket): Make wsock_evt public. * fhandler_socket.cc (fhandler_socket::fhandler_socket): Accomodate reordering members. (fhandler_socket::evaluate_events): Drop FD_CONNECT event as soon as it gets read once. Never remove FD_WRITE event here. (fhandler_socket::wait_for_events): Wait 50 ms instead of INFINITE for socket events. (fhandler_socket::accept): Fix conditional. Set wsock_events members of accepted socket to useful start values. (fhandler_socket::recv_internal): Always drop FD_READ/FD_OOB events from wsock_events after the call to WSARecvFrom. (fhandler_socket::send_internal): Drop FD_WRITE event from wsock_events if the call to WSASendTo fails with WSAEWOULDBLOCK. Fix return value condition. * select.cc (struct socketinf): Change to accomodate using socket event handling. (peek_socket): Use event handling for peeking socket. (thread_socket): Ditto. (start_thread_socket): Ditto. (socket_cleanup): Same here. * tlsoffsets.h: Regenerate. --- winsup/cygwin/ChangeLog | 28 + winsup/cygwin/cygtls.cc | 272 +++++++ winsup/cygwin/cygtls.h | 260 ++++++ winsup/cygwin/fhandler.h | 6 +- winsup/cygwin/fhandler_socket.cc | 62 +- winsup/cygwin/select.cc | 1647 ++++++++++++++++++++++++++++++++++++++ winsup/cygwin/tlsoffsets.h | 129 +++ 7 files changed, 2380 insertions(+), 24 deletions(-) create mode 100644 winsup/cygwin/cygtls.cc create mode 100644 winsup/cygwin/cygtls.h create mode 100644 winsup/cygwin/select.cc create mode 100644 winsup/cygwin/tlsoffsets.h diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 9b6a3f20421..144957a6faa 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,31 @@ +2006-07-25 Corinna Vinschen + + * cygtls.h: Drop socket related includes. + (struct _local_storage): Remove exitsock and exitsock_sin. Add + select_sockevt. + * cygtls.cc: Accomodate above change throughout. + * fhandler.h (class fhandler_socket): Make wsock_evt public. + * fhandler_socket.cc (fhandler_socket::fhandler_socket): Accomodate + reordering members. + (fhandler_socket::evaluate_events): Drop FD_CONNECT event as soon as + it gets read once. Never remove FD_WRITE event here. + (fhandler_socket::wait_for_events): Wait 50 ms instead of INFINITE for + socket events. + (fhandler_socket::accept): Fix conditional. Set wsock_events members + of accepted socket to useful start values. + (fhandler_socket::recv_internal): Always drop FD_READ/FD_OOB events from + wsock_events after the call to WSARecvFrom. + (fhandler_socket::send_internal): Drop FD_WRITE event from wsock_events + if the call to WSASendTo fails with WSAEWOULDBLOCK. Fix return value + condition. + * select.cc (struct socketinf): Change to accomodate using socket event + handling. + (peek_socket): Use event handling for peeking socket. + (thread_socket): Ditto. + (start_thread_socket): Ditto. + (socket_cleanup): Same here. + * tlsoffsets.h: Regenerate. + 2006-07-23 Christopher Faylor * include/cygwin/version.h: Bump DLL minor version number to 22. diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc new file mode 100644 index 00000000000..ce794cd382b --- /dev/null +++ b/winsup/cygwin/cygtls.cc @@ -0,0 +1,272 @@ +/* cygtls.cc + + Copyright 2003, 2004, 2005, 2006 Red Hat, Inc. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include +#define USE_SYS_TYPES_FD_SET +#include +#include "thread.h" +#include "cygtls.h" +#include "assert.h" +#include +#include +#include +#include "exceptions.h" +#include "sync.h" +#include "cygerrno.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "pinfo.h" +#include "sigproc.h" + +class sentry +{ + static muto lock; + int destroy; +public: + void init (); + bool acquired () {return lock.acquired ();} + sentry () {destroy = 0;} + sentry (DWORD wait) {destroy = lock.acquire (wait);} + ~sentry () {if (destroy) lock.release ();} + friend void _cygtls::init (); +}; + +muto NO_COPY sentry::lock; + +static size_t NO_COPY nthreads; + +#define THREADLIST_CHUNK 256 + +void +_cygtls::init () +{ + if (cygheap->threadlist) + memset (cygheap->threadlist, 0, cygheap->sthreads * sizeof (cygheap->threadlist[0])); + else + { + cygheap->sthreads = THREADLIST_CHUNK; + cygheap->threadlist = (_cygtls **) ccalloc (HEAP_TLS, cygheap->sthreads, + sizeof (cygheap->threadlist[0])); + } + sentry::lock.init ("sentry_lock"); +} + +/* Two calls to get the stack right... */ +void +_cygtls::call (DWORD (*func) (void *, void *), void *arg) +{ + char buf[CYGTLS_PADSIZE]; + _my_tls.call2 (func, arg, buf); +} + +void +_cygtls::call2 (DWORD (*func) (void *, void *), void *arg, void *buf) +{ + init_thread (buf, func); + DWORD res = func (arg, buf); + remove (INFINITE); + /* Don't call ExitThread on the main thread since we may have been + dynamically loaded. */ + if ((void *) func != (void *) dll_crt0_1 + && (void *) func != (void *) dll_dllcrt0_1) + ExitThread (res); +} + +void +_cygtls::init_thread (void *x, DWORD (*func) (void *, void *)) +{ + if (x) + { + memset (this, 0, sizeof (*this)); + stackptr = stack; + if (_GLOBAL_REENT) + { + local_clib._stdin = _GLOBAL_REENT->_stdin; + local_clib._stdout = _GLOBAL_REENT->_stdout; + local_clib._stderr = _GLOBAL_REENT->_stderr; + local_clib.__sdidinit = _GLOBAL_REENT->__sdidinit ? -1 : 0; + local_clib.__cleanup = _GLOBAL_REENT->__cleanup; + local_clib.__sglue._niobs = 3; + local_clib.__sglue._iobs = &_GLOBAL_REENT->__sf[0]; + } + local_clib._current_locale = "C"; + locals.process_logmask = LOG_UPTO (LOG_DEBUG); + /* Initialize this thread's ability to respond to things like + SIGSEGV or SIGFPE. */ + init_exception_handler (handle_exceptions); + } + + thread_id = GetCurrentThreadId (); + initialized = CYGTLS_INITIALIZED; + locals.select_sockevt = INVALID_HANDLE_VALUE; + errno_addr = &(local_clib._errno); + + if ((void *) func == (void *) cygthread::stub + || (void *) func == (void *) cygthread::simplestub) + return; + + if (wincap.has_security ()) + cygheap->user.reimpersonate (); + + sentry here (INFINITE); + if (nthreads >= cygheap->sthreads) + { + cygheap->threadlist = (_cygtls **) + crealloc (cygheap->threadlist, (cygheap->sthreads += THREADLIST_CHUNK) + * sizeof (cygheap->threadlist[0])); + memset (cygheap->threadlist + nthreads, 0, THREADLIST_CHUNK * sizeof (cygheap->threadlist[0])); + } + + cygheap->threadlist[nthreads++] = this; +} + +void +_cygtls::fixup_after_fork () +{ + if (sig) + { + pop (); + sig = 0; + } + stacklock = spinning = 0; + locals.select_sockevt = INVALID_HANDLE_VALUE; + wq.thread_ev = NULL; +} + +#define free_local(x) \ + if (locals.x) \ + { \ + free (locals.x); \ + locals.x = NULL; \ + } + +void +_cygtls::remove (DWORD wait) +{ + initialized = 0; + if (!locals.select_sockevt || exit_state >= ES_FINAL) + return; + + debug_printf ("wait %p", wait); + if (wait) + { + /* FIXME: Need some sort of atthreadexit function to allow things like + select to control this themselves. */ + if (locals.select_sockevt != INVALID_HANDLE_VALUE) + { + CloseHandle (locals.select_sockevt); + locals.select_sockevt = (HANDLE) NULL; + } + free_local (process_ident); + free_local (ntoa_buf); + free_local (protoent_buf); + free_local (servent_buf); + free_local (hostent_buf); + } + + do + { + sentry here (wait); + if (here.acquired ()) + { + for (size_t i = 0; i < nthreads; i++) + if (this == cygheap->threadlist[i]) + { + if (i < --nthreads) + cygheap->threadlist[i] = cygheap->threadlist[nthreads]; + debug_printf ("removed %p element %d", this, i); + break; + } + } + } while (0); + remove_wq (wait); +} + +void +_cygtls::push (__stack_t addr) +{ + *stackptr++ = (__stack_t) addr; +} + +#define BAD_IX ((size_t) -1) +static size_t NO_COPY threadlist_ix = BAD_IX; + +_cygtls * +_cygtls::find_tls (int sig) +{ + debug_printf ("sig %d\n", sig); + sentry here (INFINITE); + __asm__ volatile (".equ _threadlist_exception_return,."); + _cygtls *res = NULL; + for (threadlist_ix = 0; threadlist_ix < nthreads; threadlist_ix++) + if (sigismember (&(cygheap->threadlist[threadlist_ix]->sigwait_mask), sig)) + { + res = cygheap->threadlist[threadlist_ix]; + break; + } + threadlist_ix = BAD_IX; + return res; +} + +void +_cygtls::set_siginfo (sigpacket *pack) +{ + infodata = pack->si; +} + +extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD); +int +_cygtls::handle_threadlist_exception (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *c, void *) +{ + if (e->ExceptionCode != STATUS_ACCESS_VIOLATION) + { + system_printf ("unhandled exception %p at %p", e->ExceptionCode, c->Eip); + return 1; + } + + sentry here; + if (threadlist_ix == BAD_IX) + { + api_fatal ("called with threadlist_ix %d", BAD_IX); + return 1; + } + + if (!here.acquired ()) + { + system_printf ("couldn't aquire muto"); + return 1; + } + + extern void *threadlist_exception_return; + cygheap->threadlist[threadlist_ix]->remove (INFINITE); + threadlist_ix = 0; + RtlUnwind (frame, threadlist_exception_return, e, 0); + return 0; +} + +/* Set up the exception handler for the current thread. The x86 uses segment + register fs, offset 0 to point to the current exception handler. */ + +extern exception_list *_except_list asm ("%fs:0"); + +void +_cygtls::init_exception_handler (exception_handler *eh) +{ + el.handler = eh; + el.prev = ⪙ + _except_list = ⪙ +} + +void +_cygtls::init_threadlist_exceptions () +{ + init_exception_handler (handle_threadlist_exception); +} diff --git a/winsup/cygwin/cygtls.h b/winsup/cygwin/cygtls.h new file mode 100644 index 00000000000..d42ccc6f70e --- /dev/null +++ b/winsup/cygwin/cygtls.h @@ -0,0 +1,260 @@ +/* cygtls.h + + Copyright 2003, 2004, 2005 Red Hat, Inc. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _CYGTLS_H +#define _CYGTLS_H + +#include +#include +#include +#include +#define _NOMNTENT_FUNCS +#include +#undef _NOMNTENT_FUNCS +#include +#include + +#define CYGTLS_INITIALIZED 0xc763173f + +#ifndef CYG_MAX_PATH +# define CYG_MAX_PATH 260 +#endif + +#ifndef UNLEN +# define UNLEN 256 +#endif + +#define TLS_STACK_SIZE 256 + +#include "cygthread.h" + +#pragma pack(push,4) +struct _local_storage +{ + /* + Needed for the group functions + */ + struct __group16 grp; + char *namearray[2]; + int grp_pos; + + /* console.cc */ + unsigned rarg; + + /* dlfcn.cc */ + int dl_error; + char dl_buffer[256]; + + /* passwd.cc */ + struct passwd res; + char pass[_PASSWORD_LEN]; + int pw_pos; + + /* path.cc */ + struct mntent mntbuf; + int iteration; + unsigned available_drives; + char mnt_type[80]; + char mnt_opts[80]; + char mnt_fsname[CYG_MAX_PATH]; + char mnt_dir[CYG_MAX_PATH]; + + /* select.cc */ + HANDLE select_sockevt; + + /* strerror */ + char strerror_buf[sizeof ("Unknown error 4294967295")]; + + /* sysloc.cc */ + char *process_ident; // note: malloced + int process_logopt; + int process_facility; + int process_logmask; + + /* times.cc */ + char timezone_buf[20]; + struct tm _localtime_buf; + + /* uinfo.cc */ + char username[UNLEN + 1]; + + /* net.cc */ + char *ntoa_buf; // note: malloced + struct protoent *protoent_buf; // note: malloced + struct servent *servent_buf; // note: malloced + struct hostent *hostent_buf; // note: malloced + char signamebuf[sizeof ("Unknown signal 4294967295 ")]; + + /* cygthread.cc */ + char unknown_thread_name[30]; + + /* syscalls.cc */ + int setmode_file; + int setmode_mode; +}; + +typedef struct struct_waitq +{ + int pid; + int options; + int status; + HANDLE ev; + void *rusage; /* pointer to potential rusage */ + struct struct_waitq *next; + HANDLE thread_ev; +} waitq; + +typedef struct +{ + void *_myfault; + int _myfault_errno; +} san; + +/* Changes to the below structure may require acompanying changes to the very + simple parser in the perl script 'gentls_offsets' (<<-- start parsing here). + The union in this structure is used to force alignment between the version + of the compiler used to generate tlsoffsets.h and the cygwin cross compiler. +*/ + +/*gentls_offsets*/ +#include "cygerrno.h" + +extern "C" int __sjfault (jmp_buf); +extern "C" int __ljfault (jmp_buf, int); + +/*gentls_offsets*/ + +typedef __uint32_t __stack_t; +struct _cygtls +{ + void (*func) /*gentls_offsets*/(int)/*gentls_offsets*/; + exception_list el; + int saved_errno; + int sa_flags; + sigset_t oldmask; + sigset_t deltamask; + HANDLE event; + int *errno_addr; + sigset_t sigmask; + sigset_t sigwait_mask; + siginfo_t *sigwait_info; + struct ucontext thread_context; + DWORD thread_id; + unsigned threadkill; + siginfo_t infodata; + struct pthread *tid; + union + { + struct _reent local_clib; + char __dontuse[8 * ((sizeof(struct _reent) + 4) / 8)]; + }; + struct _local_storage locals; + class cygthread *_ctinfo; + san andreas; + waitq wq; + struct _cygtls *prev, *next; + int sig; + unsigned incyg; + unsigned spinning; + unsigned stacklock; + __stack_t *stackptr; + __stack_t stack[TLS_STACK_SIZE]; + unsigned initialized; + + /*gentls_offsets*/ + static CRITICAL_SECTION protect_linked_list; + static void init (); + void init_thread (void *, DWORD (*) (void *, void *)); + static void call (DWORD (*) (void *, void *), void *); + void call2 (DWORD (*) (void *, void *), void *, void *) __attribute__ ((regparm (3))); + static struct _cygtls *find_tls (int sig); + void remove (DWORD); + void push (__stack_t) __attribute__ ((regparm (2))); + __stack_t pop () __attribute__ ((regparm (1))); + __stack_t retaddr () {return stackptr[-1];} + bool isinitialized () const + { + volatile char here; + return ((char *) this > &here) && initialized == CYGTLS_INITIALIZED; + } + bool interrupt_now (CONTEXT *, int, void *, struct sigaction&) + __attribute__((regparm(3))); + void __stdcall interrupt_setup (int sig, void *handler, + struct sigaction& siga) + __attribute__((regparm(3))); + + /* exception handling */ + static int handle_exceptions (EXCEPTION_RECORD *, exception_list *, CONTEXT *, void *); + static int handle_threadlist_exception (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *c, void *); + void init_exception_handler (int (*) (EXCEPTION_RECORD *, exception_list *, CONTEXT *, void*)); + void init_threadlist_exceptions (); + void signal_exit (int) __attribute__ ((noreturn, regparm(2))); + void copy_context (CONTEXT *) __attribute__ ((regparm(2))); + void signal_debugger (int) __attribute__ ((regparm(2))); + +#ifdef _THREAD_H + operator HANDLE () const {return tid->win32_obj_id;} +#endif + void set_siginfo (struct sigpacket *) __attribute__ ((regparm (3))); + void set_threadkill () {threadkill = true;} + void reset_threadkill () {threadkill = false;} + int call_signal_handler () __attribute__ ((regparm (1))); + void remove_wq (DWORD) __attribute__ ((regparm (1))); + void fixup_after_fork () __attribute__ ((regparm (1))); + void lock () __attribute__ ((regparm (1))); + void unlock () __attribute__ ((regparm (1))); + bool locked () __attribute__ ((regparm (1))); + void*& fault_guarded () {return andreas._myfault;} + void return_from_fault () + { + if (andreas._myfault_errno) + set_errno (andreas._myfault_errno); + __ljfault ((int *) andreas._myfault, 1); + } + int setup_fault (jmp_buf j, san& old_j, int myerrno) __attribute__ ((always_inline)) + { + old_j._myfault = andreas._myfault; + old_j._myfault_errno = andreas._myfault_errno; + andreas._myfault = (void *) j; + andreas._myfault_errno = myerrno; + return __sjfault (j); + } + void reset_fault (san& old_j) __attribute__ ((always_inline)) + { + andreas._myfault = old_j._myfault; + andreas._myfault_errno = old_j._myfault_errno; + } + /*gentls_offsets*/ +}; +#pragma pack(pop) + +const int CYGTLS_PADSIZE = 12700; /* FIXME: Find some way to autogenerate + this value */ +/*gentls_offsets*/ + +extern char *_tlsbase __asm__ ("%fs:4"); +extern char *_tlstop __asm__ ("%fs:8"); +#define _my_tls (*((_cygtls *) (_tlsbase - CYGTLS_PADSIZE))) +extern _cygtls *_main_tls; +extern _cygtls *_sig_tls; + +class myfault +{ + jmp_buf buf; + san sebastian; +public: + ~myfault () __attribute__ ((always_inline)) { _my_tls.reset_fault (sebastian); } + inline int faulted (int myerrno = 0) __attribute__ ((always_inline)) + { + return _my_tls.setup_fault (buf, sebastian, myerrno); + } +}; + +#define __getreent() (&_my_tls.local_clib) + +#endif /*_CYGTLS_H*/ /*gentls_offsets*/ diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index afcd6db5f25..6468560b3be 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -387,13 +387,13 @@ class fhandler_socket: public fhandler_base int type; int connect_secret[4]; - HANDLE wsock_mtx; - HANDLE wsock_evt; wsa_event *wsock_events; + HANDLE wsock_mtx; public: + HANDLE wsock_evt; bool init_events (); - private: int evaluate_events (const long event_mask, long &events, bool erase); + private: int wait_for_events (const long event_mask); void release_events (); diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index f20ee09fdd8..68cf8b5633f 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -130,9 +130,9 @@ get_inet_addr (const struct sockaddr *in, int inlen, fhandler_socket::fhandler_socket () : fhandler_base (), + wsock_events (NULL), wsock_mtx (NULL), wsock_evt (NULL), - wsock_events (NULL), sun_path (NULL), status () { @@ -540,9 +540,10 @@ fhandler_socket::evaluate_events (const long event_mask, long &events, ret = SOCKET_ERROR; } wsock_events->connect_errorcode = 0; + wsock_events->events &= ~FD_CONNECT; } if (erase) - wsock_events->events &= ~(events & ~FD_CLOSE); + wsock_events->events &= ~(events & ~(FD_WRITE | FD_CLOSE)); } UNLOCK_EVENTS; @@ -567,7 +568,7 @@ fhandler_socket::wait_for_events (const long event_mask) } WSAEVENT ev[2] = { wsock_evt, signal_arrived }; - switch (WSAWaitForMultipleEvents (2, ev, FALSE, INFINITE, FALSE)) + switch (WSAWaitForMultipleEvents (2, ev, FALSE, 50, FALSE)) { case WSA_WAIT_TIMEOUT: case WSA_WAIT_EVENT_0: @@ -1107,9 +1108,9 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) int res = 0; while (!(res = wait_for_events (FD_ACCEPT | FD_CLOSE)) - && (res = ::accept (get_socket (), peer, len)) == WSAEWOULDBLOCK) + && (res = ::accept (get_socket (), peer, len)) == SOCKET_ERROR + && WSAGetLastError () == WSAEWOULDBLOCK) ; - if (res == (int) INVALID_SOCKET) set_winsock_errno (); else @@ -1140,6 +1141,9 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) } } } + /* No locking necessary at this point. */ + sock->wsock_events->events = wsock_events->events | FD_WRITE; + sock->wsock_events->owner = wsock_events->owner; sock->connect_state (connected); res = res_fd; } @@ -1253,19 +1257,29 @@ fhandler_socket::recv_internal (WSABUF *wsabuf, DWORD wsacnt, DWORD flags, { ssize_t res = 0; DWORD ret = 0; + int evt_mask = FD_READ | ((flags & MSG_OOB) ? FD_OOB : 0); flags &= MSG_WINMASK; if (flags & MSG_PEEK) - res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &ret, - &flags, from, fromlen, NULL, NULL); + { + LOCK_EVENTS; + res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &ret, + &flags, from, fromlen, NULL, NULL); + wsock_events->events &= ~evt_mask; + UNLOCK_EVENTS; + } else { - int evt_mask = FD_READ | FD_CLOSE - | ((flags & MSG_OOB) ? FD_OOB : 0); - while ((res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &ret, - &flags, from, fromlen, NULL, NULL)) == -1 - && WSAGetLastError () == WSAEWOULDBLOCK - && !(res = wait_for_events (evt_mask))) + do + { + LOCK_EVENTS; + res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &ret, + &flags, from, fromlen, NULL, NULL); + wsock_events->events &= ~evt_mask; + UNLOCK_EVENTS; + } + while (res && WSAGetLastError () == WSAEWOULDBLOCK + && !(res = wait_for_events (evt_mask | FD_CLOSE))) ; } @@ -1355,15 +1369,23 @@ fhandler_socket::send_internal (struct _WSABUF *wsabuf, DWORD wsacnt, int flags, const struct sockaddr *to, int tolen) { int res = 0; - DWORD ret = 0; - while ((res = WSASendTo (get_socket (), wsabuf, wsacnt, &ret, - flags & MSG_WINMASK, to, tolen, NULL, NULL)) == -1 - && WSAGetLastError () == WSAEWOULDBLOCK - && !(res = wait_for_events (FD_WRITE | FD_CLOSE))) - ; + DWORD ret = 0, err = 0; + + do + { + LOCK_EVENTS; + if ((res = WSASendTo (get_socket (), wsabuf, wsacnt, &ret, + flags & MSG_WINMASK, to, tolen, NULL, NULL)) + && (err = WSAGetLastError ()) == WSAEWOULDBLOCK) + wsock_events->events &= ~FD_WRITE; + UNLOCK_EVENTS; + } + while (res && err && !(res = wait_for_events (FD_WRITE | FD_CLOSE))); if (res == SOCKET_ERROR) set_winsock_errno (); + else + res = ret; /* Special handling for EPIPE and SIGPIPE. @@ -1377,8 +1399,6 @@ fhandler_socket::send_internal (struct _WSABUF *wsabuf, DWORD wsacnt, int flags, if (! (flags & MSG_NOSIGNAL)) raise (SIGPIPE); } - else - res = ret; return res; } diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc new file mode 100644 index 00000000000..4df27b2a81d --- /dev/null +++ b/winsup/cygwin/select.cc @@ -0,0 +1,1647 @@ +/* select.cc + + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005 Red Hat, Inc. + + Written by Christopher Faylor of Cygnus Solutions + cgf@cygnus.com + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +/* 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 +#include +#include + +#include +#include +#include +#include +#include +#define USE_SYS_TYPES_FD_SET +#include +#include "cygerrno.h" +#include "select.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "pinfo.h" +#include "sigproc.h" +#include "tty.h" +#include "ntdll.h" +#include "cygtls.h" +#include + +/* + * 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)->thread_errno = EBADF; \ + return -1; \ + } \ + +/* The main select code. + */ +extern "C" int +cygwin_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); + + 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); +} + +extern "C" int +pselect(int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + const struct timespec *ts, const sigset_t *set) +{ + struct timeval tv; + sigset_t oldset = myself->getsigmask (); + + myfault efault; + if (efault.faulted (EFAULT)) + return -1; + if (ts) + { + tv.tv_sec = ts->tv_sec; + tv.tv_usec = ts->tv_nsec / 1000; + } + if (set) + set_signal_mask (*set, myself->getsigmask ()); + int ret = cygwin_select (maxfds, readfds, writefds, exceptfds, + ts ? &tv : NULL); + if (set) + set_signal_mask (oldset, myself->getsigmask ()); + return ret; +} + +/* Call cleanup functions for all inspected fds. Gets rid of any + executing threads. */ +void +select_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 */ +int +select_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) + windows_used = true; + + s->next = start.next; + start.next = s; + return 1; +} + +/* The heart of select. Waits for an fd to do something interesting. */ +int +select_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)) + { + s->set_select_errno (); + 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; + } + + LONGLONG start_time = gtod.msecs (); /* 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"); + s->set_select_errno (); + 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; + bool 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 ()) + { + set_errno (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"); + + LONGLONG now = gtod.msecs (); + 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 int +set_bits (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds) +{ + int ready = 0; + fhandler_socket *sock; + 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 && (sock = me->fh->is_socket ())) + { + /* Special AF_LOCAL handling. */ + if (!me->read_ready && sock->connect_state () == connect_pending + && sock->af_local_connect () && me->read_selected) + UNIX_FD_SET (me->fd, readfds); + sock->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 ((sock = me->fh->is_socket ())) + sock->connect_state (connect_failed); + } + 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. */ +int +select_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 int +verify_true (select_record *, fd_set *, fd_set *, fd_set *) +{ + return 1; +} + +static int +verify_ok (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds) +{ + return set_bits (me, readfds, writefds, exceptfds); +} + +static int +no_startup (select_record *, select_stuff *) +{ + return 1; +} + +static int +no_verify (select_record *, fd_set *, fd_set *, fd_set *) +{ + return 0; +} + +static int +peek_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 ("%s, already ready for read", fh->get_name ()); + gotone = 1; + goto out; + } + + switch (fh->get_major ()) + { + case DEV_TTYM_MAJOR: + 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) + select_printf ("%s, select for read/except on write end of pipe", + fh->get_name ()); + 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: avail %d", fh->get_name (), n); + gotone += s->read_ready = true; + } + if (!gotone && s->fh->hit_eof ()) + { + select_printf ("%s, saw EOF", fh->get_name ()); + if (s->except_selected) + gotone += s->except_ready = true; + if (s->read_selected) + gotone += s->read_ready = true; + } + +out: + if (s->write_selected) + { + if (s->write_ready) + { + select_printf ("%s, already ready for write", fh->get_name ()); + gotone++; + } + /* Do we need to do anything about SIGTTOU here? */ + else if (fh->get_device () == FH_PIPER) + select_printf ("%s, select for write on read end of pipe", + fh->get_name ()); + else + { +#if 0 +/* FIXME: This code is not quite correct. There's no better solution + so far but to always treat the write side of the pipe as writable. */ + + /* We don't worry about the guard mutex, because that only applies + when from_select is false, and peek_pipe is never called that + way for writes. */ + + IO_STATUS_BLOCK iosb = {0}; + FILE_PIPE_LOCAL_INFORMATION fpli = {0}; + + if (NtQueryInformationFile (h, + &iosb, + &fpli, + sizeof (fpli), + FilePipeLocalInformation)) + { + /* If NtQueryInformationFile fails, optimistically assume the + pipe is writable. This could happen on Win9x, because + NtQueryInformationFile is not available, or if we somehow + inherit a pipe that doesn't permit FILE_READ_ATTRIBUTES + access on the write end. */ + select_printf ("%s, NtQueryInformationFile failed", + fh->get_name ()); + gotone += s->write_ready = true; + } + /* Ensure that enough space is available for atomic writes, + as required by POSIX. Subsequent writes with size > PIPE_BUF + can still block, but most (all?) UNIX variants seem to work + this way (e.g., BSD, Linux, Solaris). */ + else if (fpli.WriteQuotaAvailable >= PIPE_BUF) + { + select_printf ("%s, ready for write: size %lu, avail %lu", + fh->get_name (), + fpli.OutboundQuota, + fpli.WriteQuotaAvailable); + gotone += s->write_ready = true; + } + /* If we somehow inherit a tiny pipe (size < PIPE_BUF), then consider + the pipe writable only if it is completely empty, to minimize the + probability that a subsequent write will block. */ + else if (fpli.OutboundQuota < PIPE_BUF && + fpli.WriteQuotaAvailable == fpli.OutboundQuota) + { + select_printf ("%s, tiny pipe: size %lu, avail %lu", + fh->get_name (), + fpli.OutboundQuota, + fpli.WriteQuotaAvailable); + gotone += s->write_ready = true; + } +#else + gotone += s->write_ready = true; +#endif + } + } + + return gotone; +} + +static int start_thread_pipe (select_record *me, select_stuff *stuff); + +struct pipeinf + { + cygthread *thread; + bool stop_thread_pipe; + select_record *start; + }; + +static DWORD WINAPI +thread_pipe (void *arg) +{ + pipeinf *pi = (pipeinf *) arg; + bool gotone = false; + DWORD sleep_time = 0; + + for (;;) + { + select_record *s = pi->start; + while ((s = s->next)) + if (s->startup == start_thread_pipe) + { + if (peek_pipe (s, true)) + gotone = true; + if (pi->stop_thread_pipe) + { + select_printf ("stopping"); + goto out; + } + } + /* Paranoid check */ + if (pi->stop_thread_pipe) + { + select_printf ("stopping from outer loop"); + break; + } + if (gotone) + break; + Sleep (sleep_time >> 3); + if (sleep_time < 80) + ++sleep_time; + } +out: + return 0; +} + +static int +start_thread_pipe (select_record *me, select_stuff *stuff) +{ + if (stuff->device_specific_pipe) + { + me->h = *((pipeinf *) stuff->device_specific_pipe)->thread; + return 1; + } + pipeinf *pi = new pipeinf; + pi->start = &stuff->start; + pi->stop_thread_pipe = false; + pi->thread = new cygthread (thread_pipe, 0, pi, "select_pipe"); + me->h = *pi->thread; + if (!me->h) + return 0; + stuff->device_specific_pipe = (void *) pi; + return 1; +} + +static void +pipe_cleanup (select_record *, select_stuff *stuff) +{ + pipeinf *pi = (pipeinf *) stuff->device_specific_pipe; + if (pi && pi->thread) + { + pi->stop_thread_pipe = true; + pi->thread->detach (); + delete pi; + stuff->device_specific_pipe = NULL; + } +} + +int +fhandler_pipe::ready_for_read (int fd, DWORD howlong) +{ + int res; + if (!howlong) + res = fhandler_base::ready_for_read (fd, howlong); + else if (!get_guard ()) + res = 1; + else + { + const HANDLE w4[2] = {get_guard (), signal_arrived}; + switch (WaitForMultipleObjects (2, w4, 0, INFINITE)) + { + case WAIT_OBJECT_0: + res = 1; + break; + case WAIT_OBJECT_0 + 1: + set_sig_errno (EINTR); + res = 0; + break; + default: + __seterrno (); + res = 0; + } + } + return res; +} + +select_record * +fhandler_pipe::select_read (select_record *s) +{ + if (!s) + s = new select_record; + s->startup = start_thread_pipe; + s->peek = peek_pipe; + s->verify = verify_ok; + s->cleanup = pipe_cleanup; + s->read_selected = true; + s->read_ready = false; + return s; +} + +select_record * +fhandler_pipe::select_write (select_record *s) +{ + if (!s) + s = new select_record; + s->startup = start_thread_pipe; + s->peek = peek_pipe; + s->verify = verify_ok; + s->cleanup = pipe_cleanup; + s->write_selected = true; + s->write_ready = false; + return s; +} + +select_record * +fhandler_pipe::select_except (select_record *s) +{ + if (!s) + s = new select_record; + s->startup = start_thread_pipe; + s->peek = peek_pipe; + s->verify = verify_ok; + s->cleanup = pipe_cleanup; + s->except_selected = true; + s->except_ready = false; + return s; +} + +static int +peek_console (select_record *me, bool) +{ + extern const char * get_nonascii_key (INPUT_RECORD& input_rec, char *); + fhandler_console *fh = (fhandler_console *) me->fh; + + if (!me->read_selected) + return me->write_ready; + + if (fh->get_readahead_valid ()) + { + select_printf ("readahead"); + return me->read_ready = true; + } + + if (me->read_ready) + { + select_printf ("already ready"); + return 1; + } + + INPUT_RECORD irec; + DWORD events_read; + HANDLE h; + char tmpbuf[17]; + set_handle_or_return_if_not_open (h, me); + + for (;;) + if (fh->bg_check (SIGTTIN) <= bg_eof) + return me->read_ready = true; + else if (!PeekConsoleInput (h, &irec, 1, &events_read) || !events_read) + break; + else + { + if (irec.EventType == KEY_EVENT) + { + if (irec.Event.KeyEvent.bKeyDown + && (irec.Event.KeyEvent.uChar.AsciiChar + || get_nonascii_key (irec, tmpbuf))) + return me->read_ready = true; + } + else + { + fh->send_winch_maybe (); + if (irec.EventType == MOUSE_EVENT + && fh->mouse_aware () + && (irec.Event.MouseEvent.dwEventFlags == 0 + || irec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)) + return me->read_ready = true; + } + + /* Read and discard the event */ + ReadConsoleInput (h, &irec, 1, &events_read); + } + + return me->write_ready; +} + +static int +verify_console (select_record *me, fd_set *rfds, fd_set *wfds, + fd_set *efds) +{ + return peek_console (me, true); +} + + +select_record * +fhandler_console::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_console; + set_cursor_maybe (); + } + + s->peek = peek_console; + s->h = get_handle (); + s->read_selected = true; + s->read_ready = false; + return s; +} + +select_record * +fhandler_console::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + set_cursor_maybe (); + } + + s->peek = peek_console; + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_console::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + set_cursor_maybe (); + } + + s->peek = peek_console; + s->except_selected = true; + s->except_ready = false; + return s; +} + +select_record * +fhandler_tty_common::select_read (select_record *s) +{ + return ((fhandler_pipe *) this)->fhandler_pipe::select_read (s); +} + +select_record * +fhandler_tty_common::select_write (select_record *s) +{ + return ((fhandler_pipe *) this)->fhandler_pipe::select_write (s); +} + +select_record * +fhandler_tty_common::select_except (select_record *s) +{ + return ((fhandler_pipe *) this)->fhandler_pipe::select_except (s); +} + +static int +verify_tty_slave (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds) +{ + if (WaitForSingleObject (me->h, 0) == WAIT_OBJECT_0) + me->read_ready = true; + return set_bits (me, readfds, writefds, exceptfds); +} + +select_record * +fhandler_tty_slave::select_read (select_record *s) +{ + if (!s) + s = new select_record; + s->h = input_available_event; + s->startup = no_startup; + s->peek = peek_pipe; + s->verify = verify_tty_slave; + s->read_selected = true; + s->read_ready = false; + s->cleanup = NULL; + return s; +} + +select_record * +fhandler_dev_null::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + } + s->h = get_handle (); + s->read_selected = true; + s->read_ready = true; + return s; +} + +select_record * +fhandler_dev_null::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + } + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_dev_null::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + } + s->h = get_handle (); + s->except_selected = true; + s->except_ready = false; + return s; +} + +static int start_thread_serial (select_record *me, select_stuff *stuff); + +struct serialinf + { + cygthread *thread; + bool stop_thread_serial; + select_record *start; + }; + +static int +peek_serial (select_record *s, bool) +{ + COMSTAT st; + + fhandler_serial *fh = (fhandler_serial *) s->fh; + + if (fh->get_readahead_valid () || fh->overlapped_armed < 0) + return s->read_ready = true; + + select_printf ("fh->overlapped_armed %d", fh->overlapped_armed); + + HANDLE h; + set_handle_or_return_if_not_open (h, s); + int ready = 0; + + if (s->read_selected && s->read_ready || (s->write_selected && s->write_ready)) + { + select_printf ("already ready"); + ready = 1; + goto out; + } + + SetCommMask (h, EV_RXCHAR); + + if (!fh->overlapped_armed) + { + COMSTAT st; + + ResetEvent (fh->io_status.hEvent); + + if (!ClearCommError (h, &fh->ev, &st)) + { + debug_printf ("ClearCommError"); + goto err; + } + else if (st.cbInQue) + return s->read_ready = true; + else if (WaitCommEvent (h, &fh->ev, &fh->io_status)) + return s->read_ready = true; + else if (GetLastError () == ERROR_IO_PENDING) + fh->overlapped_armed = 1; + else + { + debug_printf ("WaitCommEvent"); + goto err; + } + } + + HANDLE w4[2]; + DWORD to; + + w4[0] = fh->io_status.hEvent; + w4[1] = signal_arrived; + to = 10; + + switch (WaitForMultipleObjects (2, w4, FALSE, to)) + { + case WAIT_OBJECT_0: + if (!ClearCommError (h, &fh->ev, &st)) + { + debug_printf ("ClearCommError"); + goto err; + } + else if (!st.cbInQue) + Sleep (to); + else + { + return s->read_ready = true; + select_printf ("got something"); + } + break; + case WAIT_OBJECT_0 + 1: + select_printf ("interrupt"); + set_sig_errno (EINTR); + ready = -1; + break; + case WAIT_TIMEOUT: + break; + default: + debug_printf ("WaitForMultipleObjects"); + goto err; + } + +out: + return ready; + +err: + if (GetLastError () == ERROR_OPERATION_ABORTED) + { + select_printf ("operation aborted"); + return ready; + } + + s->set_select_errno (); + select_printf ("error %E"); + return -1; +} + +static DWORD WINAPI +thread_serial (void *arg) +{ + serialinf *si = (serialinf *) arg; + bool gotone = false; + + for (;;) + { + select_record *s = si->start; + while ((s = s->next)) + if (s->startup == start_thread_serial) + { + if (peek_serial (s, true)) + gotone = true; + } + if (si->stop_thread_serial) + { + select_printf ("stopping"); + break; + } + if (gotone) + break; + } + + select_printf ("exiting"); + return 0; +} + +static int +start_thread_serial (select_record *me, select_stuff *stuff) +{ + if (stuff->device_specific_serial) + { + me->h = *((serialinf *) stuff->device_specific_serial)->thread; + return 1; + } + serialinf *si = new serialinf; + si->start = &stuff->start; + si->stop_thread_serial = false; + si->thread = new cygthread (thread_serial, 0, si, "select_serial"); + me->h = *si->thread; + stuff->device_specific_serial = (void *) si; + return 1; +} + +static void +serial_cleanup (select_record *, select_stuff *stuff) +{ + serialinf *si = (serialinf *) stuff->device_specific_serial; + if (si && si->thread) + { + si->stop_thread_serial = true; + si->thread->detach (); + delete si; + stuff->device_specific_serial = NULL; + } +} + +select_record * +fhandler_serial::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_serial; + s->verify = verify_ok; + s->cleanup = serial_cleanup; + } + s->peek = peek_serial; + s->read_selected = true; + s->read_ready = false; + return s; +} + +select_record * +fhandler_serial::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->peek = peek_serial; + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_serial::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = NULL; + s->peek = peek_serial; + s->except_selected = false; // Can't do this + s->except_ready = false; + return s; +} + +int +fhandler_base::ready_for_read (int fd, DWORD howlong) +{ + bool avail = false; + select_record me (this); + me.fd = fd; + while (!avail) + { + select_read (&me); + avail = me.read_ready ?: me.peek (&me, false); + + if (fd >= 0 && cygheap->fdtab.not_open (fd)) + { + set_sig_errno (EBADF); + avail = false; + break; + } + + if (howlong != INFINITE) + { + if (!avail) + set_sig_errno (EAGAIN); + break; + } + + if (WaitForSingleObject (signal_arrived, avail ? 0 : 10) == WAIT_OBJECT_0) + { + debug_printf ("interrupted"); + set_sig_errno (EINTR); + avail = false; + break; + } + } + + if (get_guard () && !avail && me.read_ready) + ReleaseMutex (get_guard ()); + + select_printf ("read_ready %d, avail %d", me.read_ready, avail); + return avail; +} + +select_record * +fhandler_base::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = get_handle (); + s->read_selected = true; + s->read_ready = true; + return s; +} + +select_record * +fhandler_base::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_base::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = NULL; + s->except_selected = true; + s->except_ready = false; + return s; +} + +static int +peek_socket (select_record *me, bool) +{ + fhandler_socket *fh = (fhandler_socket *) me->fh; + long events; + long evt_mask = (FD_CLOSE + | (me->read_selected ? (FD_READ | FD_ACCEPT) : 0) + | (me->write_selected ? (FD_WRITE | FD_CONNECT) : 0) + | (me->except_selected ? (FD_OOB | FD_CONNECT) : 0)); + int ret = fh->evaluate_events (evt_mask, events, false); + if (me->read_selected) + me->read_ready |= !!(events & (FD_READ | FD_ACCEPT | FD_CLOSE)); + if (me->write_selected) + { + if ((events & FD_CONNECT) && !ret) + me->write_ready = true; + else + me->write_ready |= !!(events & (FD_WRITE | FD_CLOSE)); + } + if (me->except_selected) + me->except_ready |= ret || !!(events & (FD_OOB | FD_CLOSE)); + + return me->read_ready || me->write_ready || me->except_ready; +} + +static int start_thread_socket (select_record *, select_stuff *); + +struct socketinf + { + cygthread *thread; + int num_w4; + HANDLE w4[MAXIMUM_WAIT_OBJECTS]; + select_record *start; + }; + +static DWORD WINAPI +thread_socket (void *arg) +{ + socketinf *si = (socketinf *) arg; + bool event = false; + + select_printf ("stuff_start %p", si->start); + while (!event) + { + for (select_record *s = si->start; (s = s->next); ) + if (s->startup == start_thread_socket) + if (peek_socket (s, false)) + event = true; + if (!event) + { + switch (WaitForMultipleObjects (si->num_w4, si->w4, FALSE, 50)) + { + case WAIT_OBJECT_0: + case WAIT_FAILED: + goto out; + case WAIT_TIMEOUT: + default: + break; + } + } + } +out: + select_printf ("leaving thread_socket"); + return 0; +} + +static int +start_thread_socket (select_record *me, select_stuff *stuff) +{ + socketinf *si; + + if ((si = (socketinf *) stuff->device_specific_socket)) + { + me->h = *si->thread; + return 1; + } + + si = new socketinf; + select_record *s = &stuff->start; + if (_my_tls.locals.select_sockevt != INVALID_HANDLE_VALUE) + si->w4[0] = _my_tls.locals.select_sockevt; + else if (!(si->w4[0] = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL))) + return 1; + else + _my_tls.locals.select_sockevt = si->w4[0]; + si->num_w4 = 1; + while ((s = s->next)) + if (s->startup == start_thread_socket) + { + HANDLE evt = ((fhandler_socket *) me->fh)->wsock_evt; + /* No event/socket should show up multiple times. */ + for (int i = 1; i < si->num_w4; ++i) + if (si->w4[i] == evt) + goto continue_outer_loop; + if (si->num_w4 < MAXIMUM_WAIT_OBJECTS) + si->w4[si->num_w4++] = evt; + else /* for now */ + goto err; + continue_outer_loop: + ; + } + stuff->device_specific_socket = (void *) si; + si->start = &stuff->start; + select_printf ("stuff_start %p", &stuff->start); + si->thread = new cygthread (thread_socket, 0, si, "select_socket"); + me->h = *si->thread; + return 1; + +err: + CloseHandle (si->w4[0]); + return 0; +} + +void +socket_cleanup (select_record *, select_stuff *stuff) +{ + socketinf *si = (socketinf *) stuff->device_specific_socket; + select_printf ("si %p si->thread %p", si, si ? si->thread : NULL); + if (si && si->thread) + { + SetEvent (si->w4[0]); + /* Wait for thread to go away */ + si->thread->detach (); + ResetEvent (si->w4[0]); + stuff->device_specific_socket = NULL; + delete si; + } + select_printf ("returning"); +} + +select_record * +fhandler_socket::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_socket; + s->verify = verify_true; + s->cleanup = socket_cleanup; + } + s->peek = peek_socket; + s->read_ready = saw_shutdown_read (); + s->read_selected = true; + return s; +} + +select_record * +fhandler_socket::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_socket; + s->verify = verify_true; + s->cleanup = socket_cleanup; + } + s->peek = peek_socket; + s->write_ready = saw_shutdown_write () || connect_state () == unconnected; + s->write_selected = true; + if (connect_state () != unconnected) + { + s->except_ready = saw_shutdown_write () || saw_shutdown_read (); + s->except_on_write = true; + } + return s; +} + +select_record * +fhandler_socket::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_socket; + s->verify = verify_true; + s->cleanup = socket_cleanup; + } + s->peek = peek_socket; + /* FIXME: Is this right? Should these be used as criteria for except? */ + s->except_ready = saw_shutdown_write () || saw_shutdown_read (); + s->except_selected = true; + return s; +} + +static int +peek_windows (select_record *me, bool) +{ + MSG m; + HANDLE h; + set_handle_or_return_if_not_open (h, me); + + if (me->read_selected && me->read_ready) + return 1; + + if (PeekMessage (&m, (HWND) h, 0, 0, PM_NOREMOVE)) + { + me->read_ready = true; + select_printf ("window %d(%p) ready", me->fd, me->fh->get_handle ()); + return 1; + } + + select_printf ("window %d(%p) not ready", me->fd, me->fh->get_handle ()); + return me->write_ready; +} + +static int +verify_windows (select_record *me, fd_set *rfds, fd_set *wfds, + fd_set *efds) +{ + return peek_windows (me, true); +} + +select_record * +fhandler_windows::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + } + s->verify = verify_windows; + s->peek = peek_windows; + s->read_selected = true; + s->read_ready = false; + s->h = get_handle (); + s->windows_handle = true; + return s; +} + +select_record * +fhandler_windows::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->peek = peek_windows; + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + s->windows_handle = true; + return s; +} + +select_record * +fhandler_windows::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->peek = peek_windows; + s->h = get_handle (); + s->except_selected = true; + s->except_ready = false; + s->windows_handle = true; + return s; +} + +static int +peek_mailslot (select_record *me, bool) +{ + HANDLE h; + set_handle_or_return_if_not_open (h, me); + + if (me->read_selected && me->read_ready) + return 1; + DWORD msgcnt = 0; + if (!GetMailslotInfo (h, NULL, NULL, &msgcnt, NULL)) + { + select_printf ("mailslot %d(%p) error %E", me->fd, h); + return 1; + } + if (msgcnt > 0) + { + me->read_ready = true; + select_printf ("mailslot %d(%p) ready", me->fd, h); + return 1; + } + select_printf ("mailslot %d(%p) not ready", me->fd, h); + return 0; +} + +static int +verify_mailslot (select_record *me, fd_set *rfds, fd_set *wfds, + fd_set *efds) +{ + return peek_mailslot (me, true); +} + +static int start_thread_mailslot (select_record *me, select_stuff *stuff); + +struct mailslotinf + { + cygthread *thread; + bool stop_thread_mailslot; + select_record *start; + }; + +static DWORD WINAPI +thread_mailslot (void *arg) +{ + mailslotinf *mi = (mailslotinf *) arg; + bool gotone = false; + DWORD sleep_time = 0; + + for (;;) + { + select_record *s = mi->start; + while ((s = s->next)) + if (s->startup == start_thread_mailslot) + { + if (peek_mailslot (s, true)) + gotone = true; + if (mi->stop_thread_mailslot) + { + select_printf ("stopping"); + goto out; + } + } + /* Paranoid check */ + if (mi->stop_thread_mailslot) + { + select_printf ("stopping from outer loop"); + break; + } + if (gotone) + break; + Sleep (sleep_time >> 3); + if (sleep_time < 80) + ++sleep_time; + } +out: + return 0; +} + +static int +start_thread_mailslot (select_record *me, select_stuff *stuff) +{ + if (stuff->device_specific_mailslot) + { + me->h = *((mailslotinf *) stuff->device_specific_mailslot)->thread; + return 1; + } + mailslotinf *mi = new mailslotinf; + mi->start = &stuff->start; + mi->stop_thread_mailslot = false; + mi->thread = new cygthread (thread_mailslot, 0, mi, "select_mailslot"); + me->h = *mi->thread; + if (!me->h) + return 0; + stuff->device_specific_mailslot = (void *) mi; + return 1; +} + +static void +mailslot_cleanup (select_record *, select_stuff *stuff) +{ + mailslotinf *mi = (mailslotinf *) stuff->device_specific_mailslot; + if (mi && mi->thread) + { + mi->stop_thread_mailslot = true; + mi->thread->detach (); + delete mi; + stuff->device_specific_mailslot = NULL; + } +} + +select_record * +fhandler_mailslot::select_read (select_record *s) +{ + if (!s) + s = new select_record; + s->startup = start_thread_mailslot; + s->peek = peek_mailslot; + s->verify = verify_mailslot; + s->cleanup = mailslot_cleanup; + s->read_selected = true; + s->read_ready = false; + return s; +} diff --git a/winsup/cygwin/tlsoffsets.h b/winsup/cygwin/tlsoffsets.h new file mode 100644 index 00000000000..5fa884ecb8e --- /dev/null +++ b/winsup/cygwin/tlsoffsets.h @@ -0,0 +1,129 @@ +//;# autogenerated: Do not edit. + +//; $tls::sizeof__cygtls = 4196; +//; $tls::func = -12700; +//; $tls::pfunc = 0; +//; $tls::el = -12696; +//; $tls::pel = 4; +//; $tls::saved_errno = -12688; +//; $tls::psaved_errno = 12; +//; $tls::sa_flags = -12684; +//; $tls::psa_flags = 16; +//; $tls::oldmask = -12680; +//; $tls::poldmask = 20; +//; $tls::deltamask = -12676; +//; $tls::pdeltamask = 24; +//; $tls::event = -12672; +//; $tls::pevent = 28; +//; $tls::errno_addr = -12668; +//; $tls::perrno_addr = 32; +//; $tls::sigmask = -12664; +//; $tls::psigmask = 36; +//; $tls::sigwait_mask = -12660; +//; $tls::psigwait_mask = 40; +//; $tls::sigwait_info = -12656; +//; $tls::psigwait_info = 44; +//; $tls::thread_context = -12652; +//; $tls::pthread_context = 48; +//; $tls::thread_id = -12440; +//; $tls::pthread_id = 260; +//; $tls::threadkill = -12436; +//; $tls::pthreadkill = 264; +//; $tls::infodata = -12432; +//; $tls::pinfodata = 268; +//; $tls::tid = -12284; +//; $tls::ptid = 416; +//; $tls::local_clib = -12280; +//; $tls::plocal_clib = 420; +//; $tls::__dontuse = -12280; +//; $tls::p__dontuse = 420; +//; $tls::locals = -11216; +//; $tls::plocals = 1484; +//; $tls::_ctinfo = -9600; +//; $tls::p_ctinfo = 3100; +//; $tls::andreas = -9596; +//; $tls::pandreas = 3104; +//; $tls::wq = -9588; +//; $tls::pwq = 3112; +//; $tls::prev = -9560; +//; $tls::pprev = 3140; +//; $tls::next = -9556; +//; $tls::pnext = 3144; +//; $tls::sig = -9552; +//; $tls::psig = 3148; +//; $tls::incyg = -9548; +//; $tls::pincyg = 3152; +//; $tls::spinning = -9544; +//; $tls::pspinning = 3156; +//; $tls::stacklock = -9540; +//; $tls::pstacklock = 3160; +//; $tls::stackptr = -9536; +//; $tls::pstackptr = 3164; +//; $tls::stack = -9532; +//; $tls::pstack = 3168; +//; $tls::initialized = -8508; +//; $tls::pinitialized = 4192; +//; __DATA__ + +#define tls_func (-12700) +#define tls_pfunc (0) +#define tls_el (-12696) +#define tls_pel (4) +#define tls_saved_errno (-12688) +#define tls_psaved_errno (12) +#define tls_sa_flags (-12684) +#define tls_psa_flags (16) +#define tls_oldmask (-12680) +#define tls_poldmask (20) +#define tls_deltamask (-12676) +#define tls_pdeltamask (24) +#define tls_event (-12672) +#define tls_pevent (28) +#define tls_errno_addr (-12668) +#define tls_perrno_addr (32) +#define tls_sigmask (-12664) +#define tls_psigmask (36) +#define tls_sigwait_mask (-12660) +#define tls_psigwait_mask (40) +#define tls_sigwait_info (-12656) +#define tls_psigwait_info (44) +#define tls_thread_context (-12652) +#define tls_pthread_context (48) +#define tls_thread_id (-12440) +#define tls_pthread_id (260) +#define tls_threadkill (-12436) +#define tls_pthreadkill (264) +#define tls_infodata (-12432) +#define tls_pinfodata (268) +#define tls_tid (-12284) +#define tls_ptid (416) +#define tls_local_clib (-12280) +#define tls_plocal_clib (420) +#define tls___dontuse (-12280) +#define tls_p__dontuse (420) +#define tls_locals (-11216) +#define tls_plocals (1484) +#define tls__ctinfo (-9600) +#define tls_p_ctinfo (3100) +#define tls_andreas (-9596) +#define tls_pandreas (3104) +#define tls_wq (-9588) +#define tls_pwq (3112) +#define tls_prev (-9560) +#define tls_pprev (3140) +#define tls_next (-9556) +#define tls_pnext (3144) +#define tls_sig (-9552) +#define tls_psig (3148) +#define tls_incyg (-9548) +#define tls_pincyg (3152) +#define tls_spinning (-9544) +#define tls_pspinning (3156) +#define tls_stacklock (-9540) +#define tls_pstacklock (3160) +#define tls_stackptr (-9536) +#define tls_pstackptr (3164) +#define tls_stack (-9532) +#define tls_pstack (3168) +#define tls_initialized (-8508) +#define tls_pinitialized (4192) -- cgit v1.2.1