I've refactored the read_with_timeout() code to account for observations
about both WAIT_ABANDONED and the short race between ReadFile, WaitFor...
CancelIo and GetCompletion.
It passes regressions which isn't saying much, since a number of edge cases
in the code are fixed which never appeared in the regression testall framework.
I need 1) feedback, and 2) opinions...
[ ] code's ready to be adopted as our 'write_with_timeout' logic
[ ] code should be backported to 1.2.x now
[ ] code should be backported to 0.9.x now
[ ] code should be rolled out in the 0.9.11 release already
Bill
--- Begin Message ---
Author: wrowe
Date: Thu Mar 23 13:49:49 2006
New Revision: 388281
URL: http://svn.apache.org/viewcvs?rev=388281&view=rev
Log:
Part 1 of many read_with_timeout logic fixes. Stop polluting one
occurance of rv with the boolean result of ReadFile() to increase
the legibility of the success/failure of ReadFile. This requires
us to defer *nbytes assignment to the function's end.
This fix catches additional cases of APR_EOF, as we had not
tested this case from the error handling path. So any deferred
read of zero bytes previously returned 0 bytes APR_SUCCESS
rather than APR_EOF. (This occurs when we wait to discover the
owner of the write end closes it without additional data)
Modified:
apr/apr/trunk/file_io/win32/readwrite.c
Modified: apr/apr/trunk/file_io/win32/readwrite.c
URL:
http://svn.apache.org/viewcvs/apr/apr/trunk/file_io/win32/readwrite.c?rev=388281&r1=388280&r2=388281&view=diff
==============================================================================
--- apr/apr/trunk/file_io/win32/readwrite.c (original)
+++ apr/apr/trunk/file_io/win32/readwrite.c Thu Mar 23 13:49:49 2006
@@ -72,11 +72,11 @@
file->pOverlapped->OffsetHigh = (DWORD)(file->filePtr >> 32);
}
- rv = ReadFile(file->filehand, buf, len,
- &bytesread, file->pOverlapped);
- *nbytes = bytesread;
-
- if (!rv) {
+ if (ReadFile(file->filehand, buf, len,
+ &bytesread, file->pOverlapped)) {
+ rv = APR_SUCCESS;
+ }
+ else {
rv = apr_get_os_error();
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
/* Wait for the pending i/o */
@@ -117,16 +117,16 @@
/* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
rv = APR_EOF;
}
- } else {
- /* OK and 0 bytes read ==> end of file */
- if (*nbytes == 0)
- rv = APR_EOF;
- else
- rv = APR_SUCCESS;
+ }
+
+ /* OK and 0 bytes read ==> end of file */
+ if (rv == APR_SUCCESS && bytesread == 0)
+ rv = APR_EOF;
}
if (rv == APR_SUCCESS && file->pOverlapped && !file->pipe) {
- file->filePtr += *nbytes;
+ file->filePtr += bytesread;
}
+ *nbytes = bytesread;
return rv;
}
--- End Message ---
--- Begin Message ---
Author: wrowe
Date: Thu Mar 23 13:51:11 2006
New Revision: 388282
URL: http://svn.apache.org/viewcvs?rev=388282&view=rev
Log:
Part 2 of the necessary read_with_timeout() fixes. Catch the
condition where our broken pipe occurs durring the deferred
i/o completion phase.
Modified:
apr/apr/trunk/file_io/win32/readwrite.c
Modified: apr/apr/trunk/file_io/win32/readwrite.c
URL:
http://svn.apache.org/viewcvs/apr/apr/trunk/file_io/win32/readwrite.c?rev=388282&r1=388281&r2=388282&view=diff
==============================================================================
--- apr/apr/trunk/file_io/win32/readwrite.c (original)
+++ apr/apr/trunk/file_io/win32/readwrite.c Thu Mar 23 13:51:11 2006
@@ -113,7 +113,7 @@
}
}
}
- else if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
+ if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
/* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
rv = APR_EOF;
}
.
--- End Message ---
--- Begin Message ---
Author: wrowe
Date: Thu Mar 23 14:44:16 2006
New Revision: 388285
URL: http://svn.apache.org/viewcvs?rev=388285&view=rev
Log:
Minor typo fix to previous commit.
Modified:
apr/apr/trunk/file_io/win32/readwrite.c
Modified: apr/apr/trunk/file_io/win32/readwrite.c
URL:
http://svn.apache.org/viewcvs/apr/apr/trunk/file_io/win32/readwrite.c?rev=388285&r1=388284&r2=388285&view=diff
==============================================================================
--- apr/apr/trunk/file_io/win32/readwrite.c (original)
+++ apr/apr/trunk/file_io/win32/readwrite.c Thu Mar 23 14:44:16 2006
@@ -122,7 +122,7 @@
/* OK and 0 bytes read ==> end of file */
if (rv == APR_SUCCESS && bytesread == 0)
rv = APR_EOF;
- }
+
if (rv == APR_SUCCESS && file->pOverlapped && !file->pipe) {
file->filePtr += bytesread;
}
.
--- End Message ---
--- Begin Message ---
Author: wrowe
Date: Thu Mar 23 15:19:32 2006
New Revision: 388292
URL: http://svn.apache.org/viewcvs?rev=388292&view=rev
Log:
Part three of the read_with_timeout refactoring.
Loop on the WaitForSingleObject if it indicated WAIT_ABANDONED,
which occurs when the thread/proc which created the event exits,
and ownership of the event has been transfered.
Always try to CancelIo if the wait has failed.
Ignore the Wait/Cancel results and then always check the completion
status of the original Read. Indicate TIMEUP when appropriate.
Modified:
apr/apr/trunk/file_io/win32/readwrite.c
Modified: apr/apr/trunk/file_io/win32/readwrite.c
URL:
http://svn.apache.org/viewcvs/apr/apr/trunk/file_io/win32/readwrite.c?rev=388292&r1=388291&r2=388292&view=diff
==============================================================================
--- apr/apr/trunk/file_io/win32/readwrite.c (original)
+++ apr/apr/trunk/file_io/win32/readwrite.c Thu Mar 23 15:19:32 2006
@@ -31,6 +31,7 @@
static apr_status_t read_with_timeout(apr_file_t *file, void *buf, apr_size_t
len_in, apr_size_t *nbytes)
{
apr_status_t rv;
+ DWORD res;
DWORD len = (DWORD)len_in;
DWORD bytesread = 0;
@@ -79,38 +80,43 @@
else {
rv = apr_get_os_error();
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
- /* Wait for the pending i/o */
- if (file->timeout > 0) {
- /* timeout in milliseconds... */
- rv = WaitForSingleObject(file->pOverlapped->hEvent,
- (DWORD)(file->timeout/1000));
- }
- else if (file->timeout == -1) {
- rv = WaitForSingleObject(file->pOverlapped->hEvent, INFINITE);
+ /* Wait for the pending i/o, timeout converted from us to ms
+ * Note that we loop if someone gives up the event, since
+ * folks suggest that WAIT_ABANDONED isn't actually a result
+ * but an alert that ownership of the event has passed from
+ * one owner to a new proc/thread.
+ */
+ do {
+ res = WaitForSingleObject(file->pOverlapped->hEvent,
+ (file->timeout > 0)
+ ? (DWORD)(file->timeout/1000)
+ : ((file->timeout == -1)
+ ? INFINITE : 0));
+ } while (res == WAIT_ABANDONED);
+
+ /* There is one case that represents entirely
+ * successful operations, otherwise we will cancel
+ * the operation in progress.
+ */
+ if (res != WAIT_OBJECT_0) {
+ CancelIo(file->filehand);
}
- switch (rv) {
- case WAIT_OBJECT_0:
- GetOverlappedResult(file->filehand, file->pOverlapped,
- &bytesread, TRUE);
- rv = APR_SUCCESS;
- break;
-
- case WAIT_TIMEOUT:
- rv = APR_TIMEUP;
- break;
-
- case WAIT_FAILED:
- rv = apr_get_os_error();
- break;
- default:
- break;
+ /* Ignore any failures above. Attempt to complete
+ * the overlapped operation and use only _its_ result.
+ * For example, CancelIo or WaitForSingleObject can
+ * fail if the handle is closed, yet the read may have
+ * completed before we attempted to CancelIo...
+ */
+ if (GetOverlappedResult(file->filehand, file->pOverlapped,
+ &bytesread, TRUE)) {
+ rv = APR_SUCCESS;
}
-
- if (rv != APR_SUCCESS) {
- if (apr_os_level >= APR_WIN_98) {
- CancelIo(file->filehand);
- }
+ else {
+ rv = apr_get_os_error();
+ if (rv == APR_FROM_OS_ERROR(ERROR_IO_INCOMPLETE)
+ && res == WAIT_TIMEOUT)
+ rv = APR_TIMEUP;
}
}
if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
--- End Message ---