summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <vinschen@redhat.com>2006-07-25 17:02:33 +0000
committerCorinna Vinschen <vinschen@redhat.com>2006-07-25 17:02:33 +0000
commit3222f6ee191c0410a5676fdb3ba2fec093dbca32 (patch)
treef64a1e425f95c8192725941972c678c0d8965fa6
parentd00662336e5beb37048a8b1bb3b245affdc824f1 (diff)
downloadgdb-3222f6ee191c0410a5676fdb3ba2fec093dbca32.tar.gz
* 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.
-rw-r--r--winsup/cygwin/ChangeLog28
-rw-r--r--winsup/cygwin/cygtls.cc272
-rw-r--r--winsup/cygwin/cygtls.h260
-rw-r--r--winsup/cygwin/fhandler.h6
-rw-r--r--winsup/cygwin/fhandler_socket.cc62
-rw-r--r--winsup/cygwin/select.cc1647
-rw-r--r--winsup/cygwin/tlsoffsets.h129
7 files changed, 2380 insertions, 24 deletions
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 <corinna@vinschen.de>
+
+ * 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 <cgf@timesys.com>
* 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 <sys/time.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "thread.h"
+#include "cygtls.h"
+#include "assert.h"
+#include <syslog.h>
+#include <signal.h>
+#include <malloc.h>
+#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 = &el;
+ _except_list = &el;
+}
+
+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 <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/time.h>
+#define _NOMNTENT_FUNCS
+#include <mntent.h>
+#undef _NOMNTENT_FUNCS
+#include <setjmp.h>
+#include <exceptions.h>
+
+#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 <sys/socket.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include <wingdi.h>
+#include <winuser.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <limits.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#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 <asm/byteorder.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)->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)