Zebediah Figura : ntoskrnl/tests: Test asynchronicity of device PnP events.

Alexandre Julliard julliard at winehq.org
Mon Jul 11 15:51:38 CDT 2022


Module: wine
Branch: master
Commit: 200d7b25eccc96a740b902aa59c86fe09c4bd3e7
URL:    https://gitlab.winehq.org/wine/wine/-/commit/200d7b25eccc96a740b902aa59c86fe09c4bd3e7

Author: Zebediah Figura <zfigura at codeweavers.com>
Date:   Sat Jul  9 12:55:15 2022 -0500

ntoskrnl/tests: Test asynchronicity of device PnP events.

---

 dlls/ntoskrnl.exe/tests/driver_pnp.c | 65 ++++++++++++++++++++++++++++++++----
 1 file changed, 59 insertions(+), 6 deletions(-)

diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c
index 972cc61c6df..a3e7329ae72 100644
--- a/dlls/ntoskrnl.exe/tests/driver_pnp.c
+++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c
@@ -41,10 +41,7 @@ static UNICODE_STRING control_symlink, bus_symlink;
 static DRIVER_OBJECT *driver_obj;
 static DEVICE_OBJECT *bus_fdo, *bus_pdo;
 
-static DWORD remove_device_count;
-static DWORD surprise_removal_count;
-static DWORD query_remove_device_count;
-static DWORD cancel_remove_device_count;
+static unsigned int remove_device_count, surprise_removal_count, query_remove_device_count, cancel_remove_device_count;
 
 struct irp_queue
 {
@@ -100,6 +97,7 @@ struct device
     UNICODE_STRING child_symlink;
     DEVICE_POWER_STATE power_state;
     struct irp_queue irp_queue;
+    HANDLE plug_event, plug_event2;
 };
 
 static struct list device_list = LIST_INIT(device_list);
@@ -255,6 +253,7 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
 
         case IRP_MN_START_DEVICE:
         {
+            static const LARGE_INTEGER wait_time = {.QuadPart = -500 * 10000};
             POWER_STATE state = {.DeviceState = PowerDeviceD0};
             NTSTATUS status;
 
@@ -271,6 +270,12 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
             state = PoSetPowerState(device_obj, DevicePowerState, state);
             todo_wine ok(state.DeviceState == device->power_state, "got previous state %u\n", state.DeviceState);
             device->power_state = PowerDeviceD0;
+
+            status = ZwWaitForSingleObject(device->plug_event, TRUE, &wait_time);
+            todo_wine ok(!status, "Failed to wait for child plug event, status %#lx.\n", status);
+            status = ZwSetEvent(device->plug_event2, NULL);
+            ok(!status, "Failed to set event, status %#lx.\n", status);
+
             ret = STATUS_SUCCESS;
             break;
         }
@@ -288,6 +293,8 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
             {
                 IoSetDeviceInterfaceState(&device->child_symlink, FALSE);
                 RtlFreeUnicodeString(&device->child_symlink);
+                ZwClose(device->plug_event);
+                ZwClose(device->plug_event2);
                 irp->IoStatus.Status = STATUS_SUCCESS;
                 IoCompleteRequest(irp, IO_NO_INCREMENT);
                 IoDeleteDevice(device->device_obj);
@@ -351,10 +358,21 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
         }
 
         case IRP_MN_SURPRISE_REMOVAL:
+        {
+            static const LARGE_INTEGER wait_time = {.QuadPart = -500 * 10000};
+            NTSTATUS status;
+
             surprise_removal_count++;
             irp_queue_clear(&device->irp_queue);
+
+            status = ZwWaitForSingleObject(device->plug_event, TRUE, &wait_time);
+            ok(!status, "Failed to wait for child plug event, status %#lx.\n", status);
+            status = ZwSetEvent(device->plug_event2, NULL);
+            ok(!status, "Failed to set event, status %#lx.\n", status);
+
             ret = STATUS_SUCCESS;
             break;
+        }
 
         case IRP_MN_QUERY_REMOVE_DEVICE:
             query_remove_device_count++;
@@ -584,7 +602,9 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
 
         case IOCTL_WINETEST_BUS_ADD_CHILD:
         {
+            static const LARGE_INTEGER wait_time = {.QuadPart = -500 * 10000};
             DEVICE_OBJECT *device_obj;
+            OBJECT_ATTRIBUTES attr;
             UNICODE_STRING string;
             struct device *device;
             NTSTATUS status;
@@ -605,6 +625,11 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
             device->device_obj = device_obj;
             device->id = id;
             device->removed = FALSE;
+            InitializeObjectAttributes(&attr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+            status = ZwCreateEvent(&device->plug_event, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, FALSE);
+            ok(!status, "Failed to create event, status %#lx.\n", status);
+            status = ZwCreateEvent(&device->plug_event2, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, FALSE);
+            ok(!status, "Failed to create event, status %#lx.\n", status);
 
             ExAcquireFastMutex(&driver_lock);
             list_add_tail(&device_list, &device->entry);
@@ -613,13 +638,26 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
             device_obj->Flags &= ~DO_DEVICE_INITIALIZING;
 
             IoInvalidateDeviceRelations(bus_pdo, BusRelations);
+            IoInvalidateDeviceRelations(bus_pdo, BusRelations);
+
+            /* Synchronize both ways, to show that the bus invalidation happens
+             * completely asynchronously and that neither thread blocks waiting
+             * for the other. */
+
+            status = ZwSetEvent(device->plug_event, NULL);
+            ok(!status, "Failed to set event, status %#lx.\n", status);
+            status = ZwWaitForSingleObject(device->plug_event2, TRUE, &wait_time);
+            ok(!status, "Failed to wait for child plug event, status %#lx.\n", status);
 
             return STATUS_SUCCESS;
         }
 
         case IOCTL_WINETEST_BUS_REMOVE_CHILD:
         {
+            static const LARGE_INTEGER wait_time = {.QuadPart = -500 * 10000};
+            HANDLE plug_event = NULL, plug_event2 = NULL;
             struct device *device;
+            NTSTATUS status;
             int id;
 
             if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(int))
@@ -631,6 +669,8 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
             {
                 if (device->id == id)
                 {
+                    plug_event = device->plug_event;
+                    plug_event2 = device->plug_event2;
                     list_remove(&device->entry);
                     device->removed = TRUE;
                     break;
@@ -638,10 +678,23 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
             }
             ExReleaseFastMutex(&driver_lock);
 
+            IoInvalidateDeviceRelations(bus_pdo, BusRelations);
             IoInvalidateDeviceRelations(bus_pdo, BusRelations);
 
-            /* The actual removal might be asynchronous; we can't test that the
-             * device is gone here. */
+            /* Synchronize both ways, to show that the bus invalidation happens
+             * completely asynchronously and that neither thread blocks waiting
+             * for the other. */
+
+            status = ZwSetEvent(plug_event, NULL);
+            todo_wine ok(!status, "Failed to set event, status %#lx.\n", status);
+            status = ZwWaitForSingleObject(plug_event2, TRUE, &wait_time);
+            todo_wine ok(!status, "Failed to wait for child plug event, status %#lx.\n", status);
+            ok(surprise_removal_count == 1, "Got %u surprise removal events.\n", surprise_removal_count);
+            /* We shouldn't get IRP_MN_REMOVE_DEVICE until all user-space
+             * handles to the device are closed (and the user-space thread is
+             * currently blocked in this ioctl and won't close its handle
+             * yet.) */
+            todo_wine ok(!remove_device_count, "Got %u remove events.\n", remove_device_count);
 
             return STATUS_SUCCESS;
         }




More information about the wine-cvs mailing list