Zebediah Figura : server: Queue a cancel IRP in the device file cancel_async callback.

Alexandre Julliard julliard at winehq.org
Thu Sep 9 15:42:31 CDT 2021


Module: wine
Branch: master
Commit: 32347fcf08440d9faad8902896df3e107699e35e
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=32347fcf08440d9faad8902896df3e107699e35e

Author: Zebediah Figura <zfigura at codeweavers.com>
Date:   Fri Sep  3 23:11:39 2021 -0500

server: Queue a cancel IRP in the device file cancel_async callback.

Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/httpapi/tests/httpapi.c |  2 ++
 dlls/ntoskrnl.exe/ntoskrnl.c | 22 ----------------------
 server/async.c               |  9 ++++++++-
 server/device.c              | 23 ++++++++++-------------
 4 files changed, 20 insertions(+), 36 deletions(-)

diff --git a/dlls/httpapi/tests/httpapi.c b/dlls/httpapi/tests/httpapi.c
index 1d1ad56e9f6..b0431b0f3fd 100644
--- a/dlls/httpapi/tests/httpapi.c
+++ b/dlls/httpapi/tests/httpapi.c
@@ -349,6 +349,8 @@ static void test_v1_server(void)
     ret = CancelIo(queue);
     ok(ret, "Failed to close queue handle, error %u.\n", GetLastError());
 
+    ret = WaitForSingleObject(ovl.hEvent, 100);
+    ok(!ret, "Got %u.\n", ret);
     ret_size = 0xdeadbeef;
     ret = GetOverlappedResult(queue, &ovl, &ret_size, FALSE);
     ok(!ret, "Expected failure.\n");
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 5c3a42c3d76..f7a9d8e8efe 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -419,17 +419,6 @@ static void *create_file_object( HANDLE handle )
 
 DECLARE_CRITICAL_SECTION(irp_completion_cs);
 
-static void WINAPI cancel_completed_irp( DEVICE_OBJECT *device, IRP *irp )
-{
-    TRACE( "(%p %p)\n", device, irp );
-
-    IoReleaseCancelSpinLock(irp->CancelIrql);
-
-    irp->IoStatus.u.Status = STATUS_CANCELLED;
-    irp->IoStatus.Information = 0;
-    IoCompleteRequest(irp, IO_NO_INCREMENT);
-}
-
 /* transfer result of IRP back to wineserver */
 static NTSTATUS WINAPI dispatch_irp_completion( DEVICE_OBJECT *device, IRP *irp, void *context )
 {
@@ -455,17 +444,6 @@ static NTSTATUS WINAPI dispatch_irp_completion( DEVICE_OBJECT *device, IRP *irp,
     }
     SERVER_END_REQ;
 
-    if (status == STATUS_MORE_PROCESSING_REQUIRED)
-    {
-        /* IRP is complete, but server may have already ordered cancel call. In such case,
-         * it will return STATUS_MORE_PROCESSING_REQUIRED, leaving the IRP alive until
-         * cancel frees it. */
-        if (irp->Cancel)
-            status = STATUS_SUCCESS;
-        else
-            IoSetCancelRoutine( irp, cancel_completed_irp );
-    }
-
     if (irp->UserBuffer != irp->AssociatedIrp.SystemBuffer)
     {
         HeapFree( GetProcessHeap(), 0, irp->UserBuffer );
diff --git a/server/async.c b/server/async.c
index 278048bc7a3..d1627069086 100644
--- a/server/async.c
+++ b/server/async.c
@@ -53,6 +53,7 @@ struct async
     unsigned int         direct_result :1;/* a flag if we're passing result directly from request instead of APC  */
     unsigned int         alerted :1;      /* fd is signaled, but we are waiting for client-side I/O */
     unsigned int         terminated :1;   /* async has been terminated */
+    unsigned int         canceled :1;     /* have we already queued cancellation for this async? */
     unsigned int         unknown_status :1; /* initial status is not known yet */
     struct completion   *completion;      /* completion associated with fd */
     apc_param_t          comp_key;        /* completion key associated with fd */
@@ -259,6 +260,7 @@ struct async *create_async( struct fd *fd, struct thread *thread, const async_da
     async->direct_result = 0;
     async->alerted       = 0;
     async->terminated    = 0;
+    async->canceled      = 0;
     async->unknown_status = 0;
     async->completion    = fd_get_completion( fd, &async->comp_key );
     async->comp_flags    = 0;
@@ -494,14 +496,19 @@ static int cancel_async( struct process *process, struct object *obj, struct thr
     struct async *async;
     int woken = 0;
 
+    /* FIXME: it would probably be nice to replace the "canceled" flag with a
+     * single LIST_FOR_EACH_ENTRY_SAFE, but currently cancelling an async can
+     * cause other asyncs to be removed via async_reselect() */
+
 restart:
     LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry )
     {
-        if (async->terminated) continue;
+        if (async->terminated || async->canceled) continue;
         if ((!obj || (get_fd_user( async->fd ) == obj)) &&
             (!thread || async->thread == thread) &&
             (!iosb || async->data.iosb == iosb))
         {
+            async->canceled = 1;
             fd_cancel_async( async->fd, async );
             woken++;
             goto restart;
diff --git a/server/device.c b/server/device.c
index 2310de43ac4..9515d238fb4 100644
--- a/server/device.c
+++ b/server/device.c
@@ -205,7 +205,7 @@ static void device_file_read( struct fd *fd, struct async *async, file_pos_t pos
 static void device_file_write( struct fd *fd, struct async *async, file_pos_t pos );
 static void device_file_flush( struct fd *fd, struct async *async );
 static void device_file_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
-static void device_file_reselect_async( struct fd *fd, struct async_queue *queue );
+static void device_file_cancel_async( struct fd *fd, struct async *async );
 static void device_file_get_volume_info( struct fd *fd, struct async *async, unsigned int info_class );
 
 static const struct object_ops device_file_ops =
@@ -243,9 +243,9 @@ static const struct fd_ops device_file_fd_ops =
     default_fd_get_file_info,         /* get_file_info */
     device_file_get_volume_info,      /* get_volume_info */
     device_file_ioctl,                /* ioctl */
-    default_fd_cancel_async,          /* cancel_async */
+    device_file_cancel_async,         /* cancel_async */
     default_fd_queue_async,           /* queue_async */
-    device_file_reselect_async        /* reselect_async */
+    default_fd_reselect_async,        /* reselect_async */
 };
 
 
@@ -687,21 +687,21 @@ static void cancel_irp_call( struct irp_call *irp )
         add_irp_to_queue( irp->file->device->manager, cancel_irp, NULL );
         release_object( cancel_irp );
     }
-
-    set_irp_result( irp, STATUS_CANCELLED, NULL, 0, 0 );
 }
 
-static void device_file_reselect_async( struct fd *fd, struct async_queue *queue )
+static void device_file_cancel_async( struct fd *fd, struct async *async )
 {
     struct device_file *file = get_fd_user( fd );
     struct irp_call *irp;
 
     LIST_FOR_EACH_ENTRY( irp, &file->requests, struct irp_call, dev_entry )
-        if (irp->iosb->status != STATUS_PENDING)
+    {
+        if (irp->async == async)
         {
             cancel_irp_call( irp );
             return;
         }
+    }
 }
 
 static struct device *create_device( struct object *root, const struct unicode_str *name,
@@ -1018,12 +1018,9 @@ DECL_HANDLER(set_irp_result)
 
     if ((irp = (struct irp_call *)get_handle_obj( current->process, req->handle, 0, &irp_call_ops )))
     {
-        if (!irp->canceled)
-            set_irp_result( irp, req->status, get_req_data(), get_req_data_size(), req->size );
-        else if(irp->user_ptr) /* cancel already queued */
-            set_error( STATUS_MORE_PROCESSING_REQUIRED );
-        else /* we may be still dispatching the IRP. don't bother queuing cancel if it's already complete */
-            irp->canceled = 0;
+        set_irp_result( irp, req->status, get_req_data(), get_req_data_size(), req->size );
+        /* we may be still dispatching the IRP. don't bother queuing cancel if it's already complete */
+        irp->canceled = 0;
         close_handle( current->process, req->handle );  /* avoid an extra round-trip for close */
         release_object( irp );
     }




More information about the wine-cvs mailing list