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