Alexandre Julliard : ntdll: Reimplement NtWriteFile.
Alexandre Julliard
julliard at wine.codeweavers.com
Sat Apr 7 05:23:16 CDT 2007
Module: wine
Branch: master
Commit: 55f3605c37f21f6cae6d22e6ecd8f1b5790d3a8a
URL: http://source.winehq.org/git/wine.git/?a=commit;h=55f3605c37f21f6cae6d22e6ecd8f1b5790d3a8a
Author: Alexandre Julliard <julliard at winehq.org>
Date: Fri Apr 6 16:06:16 2007 +0200
ntdll: Reimplement NtWriteFile.
Always try a write() before queuing an async I/O.
Handle timeout waits for synchronous I/O entirely on the client side.
Queue the final APC as a proper user APC.
---
dlls/ntdll/file.c | 178 +++++++++++++++++++++++++----------------------------
1 files changed, 85 insertions(+), 93 deletions(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index b78cb48..1fb6593 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -738,8 +738,10 @@ NTSTATUS WINAPI NtWriteFile(HANDLE hFile, HANDLE hEvent,
const void* buffer, ULONG length,
PLARGE_INTEGER offset, PULONG key)
{
- int result, unix_handle, needs_close, flags;
+ int result, unix_handle, needs_close, flags, timeout_init_done = 0;
+ struct io_timeouts timeouts;
NTSTATUS status;
+ ULONG total = 0;
enum server_fd_type type;
TRACE("(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)!\n",
@@ -751,127 +753,117 @@ NTSTATUS WINAPI NtWriteFile(HANDLE hFile, HANDLE hEvent,
&needs_close, &type, &flags );
if (status) return status;
- if (type == FD_TYPE_FILE && offset)
+ if (type == FD_TYPE_FILE && offset && offset->QuadPart != (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */ )
{
- if (flags & FD_FLAG_OVERLAPPED)
+ /* async I/O doesn't make sense on regular files */
+ while ((result = pwrite( unix_handle, buffer, length, offset->QuadPart )) == -1)
{
- /* async I/O doesn't make sense on regular files */
- while ((result = pwrite( unix_handle, buffer, length, offset->QuadPart )) == -1)
+ if (errno != EINTR)
{
- if (errno == EINTR) continue;
- if (errno == EAGAIN || errno == ESPIPE)
- {
- status = STATUS_PENDING;
- break;
- }
- result = 0;
if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
else status = FILE_GetNtStatus();
goto done;
}
- if (result >= 0)
+ }
+
+ if (!(flags & FD_FLAG_OVERLAPPED)) /* update file pointer position */
+ lseek( unix_handle, offset->QuadPart + result, SEEK_SET );
+
+ total = result;
+ status = STATUS_SUCCESS;
+ goto done;
+ }
+
+ for (;;)
+ {
+ if ((result = write( unix_handle, (const char *)buffer + total, length - total )) >= 0)
+ {
+ total += result;
+ if (total == length)
{
- io_status->Information = result;
status = STATUS_SUCCESS;
- if (hEvent) NtSetEvent( hEvent, NULL );
goto done;
}
}
else
{
- if (lseek( unix_handle, offset->QuadPart, SEEK_SET ) == (off_t)-1)
+ if (errno == EINTR) continue;
+ if (errno != EAGAIN)
{
- status = FILE_GetNtStatus();
+ if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
+ else status = FILE_GetNtStatus();
goto done;
}
}
- }
- if (flags & FD_FLAG_SEND_SHUTDOWN)
- {
- status = STATUS_PIPE_DISCONNECTED;
- goto done;
- }
-
- if (flags & FD_FLAG_TIMEOUT)
- {
- if (hEvent)
+ if (flags & FD_FLAG_OVERLAPPED)
{
- /* this shouldn't happen, but check it */
- FIXME("NIY-hEvent\n");
- status = STATUS_NOT_IMPLEMENTED;
- goto done;
- }
- status = NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, NULL, 0, 0);
- if (status) goto done;
- }
-
- if (flags & (FD_FLAG_OVERLAPPED|FD_FLAG_TIMEOUT))
- {
- async_fileio* fileio;
+ async_fileio *fileio;
- if (!(fileio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(async_fileio))))
- {
- if (flags & FD_FLAG_TIMEOUT) NtClose(hEvent);
- status = STATUS_NO_MEMORY;
- goto done;
- }
- fileio->handle = hFile;
- fileio->already = 0;
- fileio->count = length;
- fileio->apc = apc;
- fileio->apc_user = apc_user;
- fileio->buffer = (void*)buffer;
- fileio->queue_apc_on_error = 0;
- fileio->avail_mode = (flags & FD_FLAG_AVAILABLE);
- fileio->event = hEvent;
-
- io_status->u.Status = STATUS_PENDING;
- status = fileio_queue_async(fileio, io_status, FALSE);
- if (status != STATUS_PENDING)
- {
- if (flags & FD_FLAG_TIMEOUT) NtClose(hEvent);
+ if (!(fileio = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(async_fileio))))
+ {
+ status = STATUS_NO_MEMORY;
+ goto done;
+ }
+ fileio->handle = hFile;
+ fileio->already = total;
+ fileio->count = length;
+ fileio->apc = apc;
+ fileio->apc_user = apc_user;
+ fileio->buffer = (void*)buffer;
+ fileio->queue_apc_on_error = 1;
+ fileio->event = hEvent;
+ status = fileio_queue_async(fileio, io_status, FALSE);
goto done;
}
- if (needs_close) close( unix_handle );
- if (flags & FD_FLAG_TIMEOUT)
+ else /* synchronous write, wait for the fd to become ready */
{
- while (NtWaitForSingleObject(hEvent, TRUE, NULL) == STATUS_USER_APC) /* nothing */ ;
- NtClose(hEvent);
- }
- else
- {
- LARGE_INTEGER timeout;
-
- /* let some APC be run, this will write as much data as possible */
- timeout.u.LowPart = timeout.u.HighPart = 0;
- status = 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 (status != STATUS_USER_APC)
- fileio->queue_apc_on_error = 1;
+ struct pollfd pfd;
+ int ret, timeout;
+
+ if (!timeout_init_done)
+ {
+ timeout_init_done = 1;
+ if ((status = get_io_timeouts( hFile, type, length, FALSE, &timeouts )))
+ goto done;
+ if (hEvent) NtResetEvent( hEvent, NULL );
+ }
+ timeout = get_next_io_timeout( &timeouts, total );
+
+ pfd.fd = unix_handle;
+ pfd.events = POLLIN;
+
+ if (!timeout || !(ret = poll( &pfd, 1, timeout )))
+ {
+ /* return with what we got so far */
+ status = total ? STATUS_SUCCESS : STATUS_TIMEOUT;
+ goto done;
+ }
+ if (ret == -1 && errno != EINTR)
+ {
+ status = FILE_GetNtStatus();
+ goto done;
+ }
+ /* will now restart the write */
}
- return io_status->u.Status;
}
- /* synchronous file write */
- status = STATUS_SUCCESS;
- while ((result = write( unix_handle, buffer, length )) == -1)
- {
- if ((errno == EAGAIN) || (errno == EINTR)) continue;
- result = 0;
- if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER;
- else status = FILE_GetNtStatus();
- break;
- }
- io_status->Information = result;
done:
if (needs_close) close( unix_handle );
- if (status != STATUS_PENDING) io_status->u.Status = status;
- if (apc && !status) apc( apc_user, io_status, 0 );
+ if (status == STATUS_SUCCESS)
+ {
+ io_status->u.Status = status;
+ io_status->Information = total;
+ TRACE("= SUCCESS (%u)\n", total);
+ if (hEvent) NtSetEvent( hEvent, NULL );
+ if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc,
+ (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 );
+ }
+ else
+ {
+ TRACE("= 0x%08x\n", status);
+ if (status != STATUS_PENDING && hEvent) NtResetEvent( hEvent, NULL );
+ }
return status;
}
More information about the wine-cvs
mailing list