diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 5912f9e8b..e833d9f94 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,46 @@ +2011-03-09 Christopher Faylor + + * errno.cc (errmap): Change mapping of NO_SYSTEM_RESOURCES to EFBIG. + * fhandler.cc (MAX_OVERLAPPED_WRITE_LEN): New constant. + (MIN_OVERLAPPED_WRITE_LEN): Ditto. + (fhandler_base_overlapped::close): Accommodate change in arguments to + wait_overlapped. + (fhandler_base_overlapped::setup_overlapped): Add __stdcall and regparm + modifiers. + (fhandler_base_overlapped::destroy_overlapped): Ditto. + (fhandler_base_overlapped::has_ongoing_io): Ditto. + (fhandler_base_overlapped::wait_overlapped): Modify to return an enum + returning various states. Accept nonblocking parameter. + (fhandler_base_overlapped::read_overlapped): Add __stdcall and regparm + modifiers. Rework to attempt to be smarter about reacting to states + returned by wait_overlapped. + (fhandler_base_overlapped::write_overlapped): Ditto. Add fallback + option for when wait_overlapped detects that smaller chunks must be + written. + (fhandler_base_overlapped::write_overlapped_fallback): Ditto. + * fhandler.h (DEFAULT_PIPEBUFSIZE): Move definition here from pipe.cc. + (fhandler_base::has_ongoing_io): Define with __stdcall and regparm + modifiers. + (fhandler_base_overlapped::wait_return): New enum. + (fhandler_base_overlapped::max_atomic_write): New variable. + (fhandler_base_overlapped:: wait_overlapped): Accommodate changes + mentioned above to arguments and modifiers. + (fhandler_base_overlapped::setup_overlapped): Ditto for modifiers. + (fhandler_base_overlapped::read_overlapped): Ditto. + (fhandler_base_overlapped::write_overlapped): Ditto. + (fhandler_base_overlapped::destroy_overlapped): Ditto. + (fhandler_base_overlapped::has_ongoing_io): Ditto. + (fhandler_base_overlapped::fhandler_base_overlapped): Zero + max_atomic_write. + * fhandler_fifo.cc (fhandler_fifo::fhandler_fifo): Set max_atomic_write + to the size of the DEFAULT_PIPEBUFSIZE. + (fhandler_fifo::wait): Accommodate change in arguments to + wait_overlapped. + * pipe.cc (fhandler_pipe::fhandler_pipe): Set max_atomic_write to the + size of the DEFAULT_PIPEBUFSIZE. + (fhandler_pipe::create_selectable): Allow minimum size of DEFAULT_PIPEBUFSIZE. + (DEFAULT_PIPEBUFSIZE): Delete here, move to fhandler.h. + 2011-03-08 Corinna Vinschen * security.cc: Fix copyright dates. diff --git a/winsup/cygwin/errno.cc b/winsup/cygwin/errno.cc index eb9fa3dfd..b83760926 100644 --- a/winsup/cygwin/errno.cc +++ b/winsup/cygwin/errno.cc @@ -118,7 +118,7 @@ static NO_COPY struct X (NO_MORE_SEARCH_HANDLES, ENFILE), X (NO_PROC_SLOTS, EAGAIN), X (NO_SIGNAL_SENT, EIO), - X (NO_SYSTEM_RESOURCES, EAGAIN), + X (NO_SYSTEM_RESOURCES, EFBIG), X (NO_TOKEN, EINVAL), X (OPEN_FAILED, EIO), X (OPEN_FILES, EAGAIN), diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 963635c70..24a67de1c 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -29,6 +29,9 @@ details. */ #include "cygtls.h" #include "sigproc.h" +#define MAX_OVERLAPPED_WRITE_LEN (64 * 1024 * 1024) +#define MIN_OVERLAPPED_WRITE_LEN (1 * 1024 * 1024) + static NO_COPY const int CHUNK_SIZE = 1024; /* Used for crlf conversions */ struct __cygwin_perfile *perfile_table; @@ -1056,8 +1059,7 @@ fhandler_base_overlapped::close () if (is_nonblocking () && io_pending) { DWORD bytes; - set_nonblocking (false); - wait_overlapped (1, !!(get_access () & GENERIC_WRITE), &bytes); + wait_overlapped (1, !!(get_access () & GENERIC_WRITE), &bytes, false); } destroy_overlapped (); return fhandler_base::close (); @@ -1686,7 +1688,7 @@ fhandler_base::fpathconf (int v) /* Overlapped I/O */ -int +int __stdcall __attribute__ ((regparm (1))) fhandler_base_overlapped::setup_overlapped () { OVERLAPPED *ov = get_overlapped_buffer (); @@ -1696,7 +1698,7 @@ fhandler_base_overlapped::setup_overlapped () return ov->hEvent ? 0 : -1; } -void +void __stdcall __attribute__ ((regparm (1))) fhandler_base_overlapped::destroy_overlapped () { OVERLAPPED *ov = get_overlapped (); @@ -1709,7 +1711,7 @@ fhandler_base_overlapped::destroy_overlapped () get_overlapped () = NULL; } -bool +bool __stdcall __attribute__ ((regparm (1))) fhandler_base_overlapped::has_ongoing_io () { if (!io_pending) @@ -1723,27 +1725,25 @@ fhandler_base_overlapped::has_ongoing_io () return false; } -int -fhandler_base_overlapped::wait_overlapped (bool inres, bool writing, DWORD *bytes, DWORD len) +fhandler_base_overlapped::wait_return __stdcall __attribute__ ((regparm (3))) +fhandler_base_overlapped::wait_overlapped (bool inres, bool writing, DWORD *bytes, bool nonblocking, DWORD len) { if (!get_overlapped ()) - return inres; - - int res = 0; + return inres ? overlapped_success : overlapped_error; DWORD err = GetLastError (); - if (is_nonblocking ()) + if (err == ERROR_NO_SYSTEM_RESOURCES) + return overlapped_fallback; + + wait_return res = overlapped_error; + if (nonblocking) { if (inres || err == ERROR_IO_PENDING) { io_pending = err == ERROR_IO_PENDING; if (writing && !inres) - *bytes = len; /* This really isn't true but it seems like - this is a corner-case for linux's - non-blocking I/O implementation. How can - you know how many bytes were written until - the I/O operation really completes? */ - res = 1; + *bytes = len; + res = overlapped_success; err = 0; } } @@ -1768,9 +1768,11 @@ fhandler_base_overlapped::wait_overlapped (bool inres, bool writing, DWORD *byte if (signalled) { debug_printf ("got a signal"); - set_errno (EINTR); + if (!_my_tls.call_signal_handler ()) + set_errno (EINTR); + else + res = overlapped_signal; *bytes = (DWORD) -1; - res = 0; err = 0; } else if (!wores) @@ -1781,7 +1783,7 @@ fhandler_base_overlapped::wait_overlapped (bool inres, bool writing, DWORD *byte else { debug_printf ("normal %s, %u bytes", writing ? "write" : "read", *bytes); - res = 1; + res = overlapped_success; err = 0; } } @@ -1793,14 +1795,13 @@ fhandler_base_overlapped::wait_overlapped (bool inres, bool writing, DWORD *byte debug_printf ("err %u", err); __seterrno_from_win_error (err); *bytes = (DWORD) -1; - res = 0; + res = overlapped_error; } else { - res = 1; - *bytes = 0; - err = 0; debug_printf ("EOF"); + *bytes = 0; + res = overlapped_success; } if (writing && (err == ERROR_NO_DATA || err == ERROR_BROKEN_PIPE)) @@ -1808,39 +1809,119 @@ fhandler_base_overlapped::wait_overlapped (bool inres, bool writing, DWORD *byte return res; } -void __stdcall +void __stdcall __attribute__ ((regparm (3))) fhandler_base_overlapped::read_overlapped (void *ptr, size_t& len) { DWORD nbytes; if (has_ongoing_io ()) nbytes = (DWORD) -1; else - while (1) - { - bool res = ReadFile (get_handle (), ptr, len, &nbytes, - get_overlapped ()); - int wres = wait_overlapped (res, false, &nbytes); - if (wres || !_my_tls.call_signal_handler ()) - break; - } + { + bool keep_looping; + do + { + bool res = ReadFile (get_handle (), ptr, len, &nbytes, + get_overlapped ()); + switch (wait_overlapped (res, false, &nbytes, is_nonblocking ())) + { + case overlapped_signal: + keep_looping = true; + break; + default: /* Added to quiet gcc */ + case overlapped_success: + case overlapped_error: + case overlapped_fallback: + keep_looping = false; + break; + } + } + while (keep_looping); + } len = (size_t) nbytes; } -ssize_t __stdcall +ssize_t __stdcall __attribute__ ((regparm (3))) fhandler_base_overlapped::write_overlapped (const void *ptr, size_t len) { DWORD nbytes; if (has_ongoing_io ()) nbytes = (DWORD) -1; else - while (1) - { - bool res = WriteFile (get_output_handle (), ptr, len, &nbytes, - get_overlapped ()); - int wres = wait_overlapped (res, true, &nbytes, (size_t) len); - if (wres || !_my_tls.call_signal_handler ()) - break; - } + { + bool keep_looping; + if (is_nonblocking () && max_atomic_write && len > max_atomic_write) + len = max_atomic_write; + do + { + bool res = WriteFile (get_output_handle (), ptr, len, &nbytes, + get_overlapped ()); + switch (wait_overlapped (res, true, &nbytes, (size_t) len)) + { + case overlapped_fallback: + nbytes = write_overlapped_fallback (ptr, len); + /* fall through intentionally */; + case overlapped_signal: + keep_looping = true; + break; + default: /* Added to quiet gcc */ + case overlapped_success: + case overlapped_error: + keep_looping = false; + break; + } + } + while (keep_looping); + } debug_printf ("returning %u", nbytes); return nbytes; } + +/* On XP (at least) the size of the buffer that can be used to write to a pipe + (pipes are currently the only thing using the overlapped methods) is + limited. This function is a fallback for when that problem is detected. + It writes to the pipe using smaller buffers but masks this behavior + to the caller. */ +ssize_t __stdcall __attribute__ ((regparm (3))) +fhandler_base_overlapped::write_overlapped_fallback (const void *ptr, size_t orig_len) +{ + size_t chunk; + if (orig_len > MAX_OVERLAPPED_WRITE_LEN) + chunk = MAX_OVERLAPPED_WRITE_LEN; + else if (orig_len > MIN_OVERLAPPED_WRITE_LEN) + chunk = MIN_OVERLAPPED_WRITE_LEN; + else + chunk = orig_len / 4; + ssize_t nbytes = 0; + DWORD nbytes_now = 0; + while ((size_t) nbytes < orig_len) + { + size_t left = orig_len - nbytes; + size_t len; + if (left > chunk) + len = chunk; + else + len = left; + bool res = WriteFile (get_output_handle (), ptr, len, &nbytes_now, + get_overlapped ()); + /* The nonblocking case is not going to be used currently and may + eventually disappear. */ + switch (wait_overlapped (res, true, &nbytes_now, + left <= chunk ? is_nonblocking () : false, + (size_t) len)) + { + case overlapped_success: + nbytes += nbytes_now; + /* fall through intentionally */ + case overlapped_signal: + break; /* keep looping */ + case overlapped_error: + case overlapped_fallback: /* Could make this more adaptive + if needed */ + orig_len = 0; /* terminate loop */ + break; + } + } + if (!nbytes) + nbytes = nbytes_now; + return nbytes; +} diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 74eebf558..ee08e5990 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -29,6 +29,7 @@ details. */ Using this blocksize in read/write calls in the application results in a much better performance than using smaller values. */ #define PREFERRED_IO_BLKSIZE ((blksize_t) 65536) +#define DEFAULT_PIPEBUFSIZE PREFERRED_IO_BLKSIZE extern const char *windows_device_names[]; extern struct __cygwin_perfile *perfile_table; @@ -397,7 +398,7 @@ public: bool issymlink () {return pc.issymlink ();} bool device_access_denied (int) __attribute__ ((regparm (2))); int fhaccess (int flags, bool) __attribute__ ((regparm (3))); - virtual bool has_ongoing_io () {return false;} + virtual bool __stdcall has_ongoing_io () __attribute__ ((regparm (1))) {return false;} }; class fhandler_mailslot : public fhandler_base @@ -565,23 +566,33 @@ class fhandler_socket: public fhandler_base class fhandler_base_overlapped: public fhandler_base { protected: + enum wait_return + { + overlapped_success = 0, + overlapped_signal, + overlapped_error, + overlapped_fallback + }; bool io_pending; OVERLAPPED io_status; OVERLAPPED *overlapped; + size_t max_atomic_write; public: - int wait_overlapped (bool, bool, DWORD *, DWORD = 0) __attribute__ ((regparm (3))); - int setup_overlapped () __attribute__ ((regparm (1))); - void destroy_overlapped () __attribute__ ((regparm (1))); + wait_return __stdcall wait_overlapped (bool, bool, DWORD *, bool, DWORD = 0) __attribute__ ((regparm (3))); + int __stdcall setup_overlapped () __attribute__ ((regparm (1))); + void __stdcall destroy_overlapped () __attribute__ ((regparm (1))); void __stdcall read_overlapped (void *ptr, size_t& len) __attribute__ ((regparm (3))); - ssize_t __stdcall write_overlapped (const void *ptr, size_t len); + ssize_t __stdcall write_overlapped (const void *ptr, size_t len) __attribute__ ((regparm (3))); + ssize_t __stdcall write_overlapped_fallback (const void *ptr, size_t orig_len) + __attribute__ ((regparm (3))); OVERLAPPED *&get_overlapped () {return overlapped;} OVERLAPPED *get_overlapped_buffer () {return &io_status;} void set_overlapped (OVERLAPPED *ov) {overlapped = ov;} - fhandler_base_overlapped (): io_pending (false), overlapped (NULL) + fhandler_base_overlapped (): io_pending (false), overlapped (NULL), max_atomic_write (0) { memset (&io_status, 0, sizeof io_status); } - bool has_ongoing_io (); + bool __stdcall has_ongoing_io () __attribute__ ((regparm (1))); void fixup_after_fork (HANDLE); void fixup_after_exec (); diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc index 746409c01..9142947ed 100644 --- a/winsup/cygwin/fhandler_fifo.cc +++ b/winsup/cygwin/fhandler_fifo.cc @@ -1,6 +1,7 @@ /* fhandler_fifo.cc - See fhandler.h for a description of the fhandler classes. - Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. + Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 + Red Hat, Inc. This file is part of Cygwin. @@ -24,6 +25,7 @@ fhandler_fifo::fhandler_fifo (): fhandler_base_overlapped (), wait_state (fifo_unknown), dummy_client (NULL) { + max_atomic_write = DEFAULT_PIPEBUFSIZE; need_fork_fixup (true); } @@ -188,7 +190,7 @@ fhandler_fifo::wait (bool iswrite) int res = ConnectNamedPipe (get_handle (), get_overlapped ()); if (GetLastError () != ERROR_NO_DATA && GetLastError () != ERROR_PIPE_CONNECTED) { - res = wait_overlapped (res, iswrite, &dummy_bytes); + res = wait_overlapped (res, iswrite, &dummy_bytes, false); if (!res) { if (get_errno () != EINTR) diff --git a/winsup/cygwin/pipe.cc b/winsup/cygwin/pipe.cc index 2f3bebe2c..2a44839f9 100644 --- a/winsup/cygwin/pipe.cc +++ b/winsup/cygwin/pipe.cc @@ -1,7 +1,7 @@ /* pipe.cc: pipe for Cygwin. Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2009, 2010 Hat, Inc. + 2008, 2009, 2010, 2011 Hat, Inc. This file is part of Cygwin. @@ -26,6 +26,7 @@ details. */ fhandler_pipe::fhandler_pipe () : fhandler_base_overlapped (), popen_pid (0) { + max_atomic_write = DEFAULT_PIPEBUFSIZE; need_fork_fixup (true); uninterruptible_io (true); } @@ -217,8 +218,8 @@ fhandler_pipe::create_selectable (LPSECURITY_ATTRIBUTES sa_ptr, HANDLE& r, r = w = INVALID_HANDLE_VALUE; /* Ensure that there is enough pipe buffer space for atomic writes. */ - if (psize < PIPE_BUF) - psize = PIPE_BUF; + if (psize < DEFAULT_PIPEBUFSIZE) + psize = DEFAULT_PIPEBUFSIZE; char pipename[MAX_PATH]; const size_t len = __small_sprintf (pipename, PIPE_INTRO "%S-", @@ -373,8 +374,6 @@ fhandler_pipe::fstatvfs (struct statvfs *sfs) return -1; } -#define DEFAULT_PIPEBUFSIZE 65536 - extern "C" int pipe (int filedes[2]) {