Fix Asynchronous File Errors (Try 2)
Robert Shearman
rob at codeweavers.com
Wed Aug 18 20:04:43 CDT 2004
Oops. Missed out the test case from the patch.
Changelog:
- Errors should only be reported to I/O completion routine after
NtReadFile has succeeded.
- Test case for this behaviour.
-------------- next part --------------
Index: wine/dlls/kernel/tests/file.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/tests/file.c,v
retrieving revision 1.41
diff -u -p -r1.41 file.c
--- wine/dlls/kernel/tests/file.c 6 Jul 2004 21:31:24 -0000 1.41
+++ wine/dlls/kernel/tests/file.c 19 Aug 2004 01:02:18 -0000
@@ -1208,6 +1208,50 @@ static void test_GetFileType(void)
DeleteFileA( filename );
}
+static int completion_count;
+
+static void CALLBACK FileIOComplete(DWORD dwError, DWORD dwBytes, LPOVERLAPPED ovl)
+{
+/* printf("(%ld, %ld, %p { %ld, %ld, %ld, %ld, %p })\n", dwError, dwBytes, ovl, ovl->Internal, ovl->InternalHigh, ovl->Offset, ovl->OffsetHigh, ovl->hEvent);*/
+ ReleaseSemaphore(ovl->hEvent, 1, NULL);
+ completion_count++;
+}
+
+static void test_async_file_errors(void)
+{
+ char szFile[MAX_PATH];
+ HANDLE hSem = CreateSemaphoreW(NULL, 1, 1, NULL);
+ HANDLE hFile;
+ LPVOID lpBuffer = HeapAlloc(GetProcessHeap(), 0, 4096);
+ OVERLAPPED ovl;
+ ovl.Offset = 0;
+ ovl.OffsetHigh = 0;
+ ovl.hEvent = hSem;
+ completion_count = 0;
+ szFile[0] = '\0';
+ GetWindowsDirectoryA(szFile, sizeof(szFile)/sizeof(szFile[0])-1-strlen("\\win.ini"));
+ strcat(szFile, "\\win.ini");
+ hFile = CreateFileA(szFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
+ ok(hFile != NULL, "CreateFileA(%s ...) failed\n", szFile);
+ while (TRUE)
+ {
+ BOOL res;
+ while (WaitForSingleObjectEx(hSem, INFINITE, TRUE) == WAIT_IO_COMPLETION)
+ ;
+ res = ReadFileEx(hFile, lpBuffer, 4096, &ovl, FileIOComplete);
+ /*printf("Offset = %ld, result = %s\n", ovl.Offset, res ? "TRUE" : "FALSE");*/
+ if (!res)
+ break;
+ ovl.Offset += 4096;
+ /* i/o completion routine only called if ReadFileEx returned success.
+ * we only care about violations of this rule so undo what should have
+ * been done */
+ completion_count--;
+ }
+ ok(completion_count == 0, "completion routine should only be called when ReadFileEx succeeds (this rule was violated %d times)\n", completion_count);
+ /*printf("Error = %ld\n", GetLastError());*/
+}
+
START_TEST(file)
{
test__hread( );
@@ -1234,4 +1278,5 @@ START_TEST(file)
test_offset_in_overlapped_structure();
test_MapFile();
test_GetFileType();
+ test_async_file_errors();
}
Index: wine/dlls/ntdll/file.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/file.c,v
retrieving revision 1.63
diff -u -p -r1.63 file.c
--- wine/dlls/ntdll/file.c 18 Aug 2004 20:57:30 -0000 1.63
+++ wine/dlls/ntdll/file.c 19 Aug 2004 01:02:18 -0000
@@ -248,6 +248,7 @@ typedef struct async_fileio
char *buffer;
unsigned int count;
off_t offset;
+ int queue_apc_on_error;
BOOL avail_mode;
} async_fileio;
@@ -265,7 +266,8 @@ static void CALLBACK fileio_call_complet
async_fileio *ovp = (async_fileio*) data;
TRACE("data: %p\n", ovp);
- ovp->apc( ovp->apc_user, ovp->async.iosb, ovp->async.iosb->Information );
+ if ((ovp->async.iosb->u.Status == STATUS_SUCCESS) || ovp->queue_apc_on_error)
+ ovp->apc( ovp->apc_user, ovp->async.iosb, ovp->async.iosb->Information );
fileio_async_cleanup( &ovp->async );
}
@@ -464,6 +467,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile,
ovp->apc = apc;
ovp->apc_user = apc_user;
ovp->buffer = buffer;
+ ovp->queue_apc_on_error = 0;
ovp->avail_mode = (flags & FD_FLAG_AVAILABLE);
NtResetEvent(hEvent, NULL);
@@ -477,8 +481,10 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile,
}
if (flags & FD_FLAG_TIMEOUT)
{
- NtWaitForSingleObject(hEvent, TRUE, NULL);
+ ret = NtWaitForSingleObject(hEvent, TRUE, NULL);
NtClose(hEvent);
+ if (ret != STATUS_USER_APC)
+ ovp->queue_apc_on_error = 1;
}
else
{
@@ -486,7 +492,14 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile,
/* let some APC be run, this will read some already pending data */
timeout.u.LowPart = timeout.u.HighPart = 0;
- NtDelayExecution( TRUE, &timeout );
+ ret = NtDelayExecution( TRUE, &timeout );
+ /* the apc didn't run and therefore the completion routine now
+ * needs to be sent errors.
+ * Note that there is no race between setting this flag and
+ * returning errors because apc's are run only during alertable
+ * waits */
+ if (ret != STATUS_USER_APC)
+ ovp->queue_apc_on_error = 1;
/* if we only have to read the available data, and none is available,
* simply cancel the request. If data was available, it has been read
* while in by previous call (NtDelayExecution)
@@ -497,6 +510,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile,
register_old_async(&ovp->async);
}
}
+ TRACE("= 0x%08lx\n", io_status->u.Status);
return io_status->u.Status;
}
@@ -522,6 +536,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile,
break;
}
wine_server_release_fd( hFile, unix_handle );
+ TRACE("= 0x%08lx\n", io_status->u.Status);
return io_status->u.Status;
}
@@ -641,6 +656,7 @@ NTSTATUS WINAPI NtWriteFile(HANDLE hFile
ovp->apc = apc;
ovp->apc_user = apc_user;
ovp->buffer = (void*)buffer;
+ ovp->queue_apc_on_error = 0;
ovp->avail_mode = (flags & FD_FLAG_AVAILABLE);
NtResetEvent(hEvent, NULL);
@@ -650,8 +666,10 @@ NTSTATUS WINAPI NtWriteFile(HANDLE hFile
return ret;
if (flags & FD_FLAG_TIMEOUT)
{
- NtWaitForSingleObject(hEvent, TRUE, NULL);
+ ret = NtWaitForSingleObject(hEvent, TRUE, NULL);
NtClose(hEvent);
+ if (ret != STATUS_USER_APC)
+ ovp->queue_apc_on_error = 1;
}
else
{
@@ -659,7 +677,14 @@ NTSTATUS WINAPI NtWriteFile(HANDLE hFile
/* let some APC be run, this will write as much data as possible */
timeout.u.LowPart = timeout.u.HighPart = 0;
- NtDelayExecution( TRUE, &timeout );
+ ret = NtDelayExecution( TRUE, &timeout );
+ /* the apc didn't run and therefore the completion routine now
+ * needs to be sent errors.
+ * Note that there is no race between setting this flag and
+ * returning errors because apc's are run only during alertable
+ * waits */
+ if (ret != STATUS_USER_APC)
+ ovp->queue_apc_on_error = 1;
}
return io_status->u.Status;
}
More information about the wine-patches
mailing list