[PATCH v4 2/2] ws2_32/tests: Add tests for terminated thread asyncs completion.

Zebediah Figura zfigura at codeweavers.com
Wed Jun 8 17:06:26 CDT 2022


On 6/6/22 16:55, Paul Gofman wrote:
> +    /* asyncs without completion port are always cancelled on thread exit. */
> +    for (i = 0; i < ARRAY_SIZE(tests_no_port); ++i)
> +    {
> +        winetest_push_context("test %u", i);
> +        memset(&io, 0xcc, sizeof(io));
> +        ResetEvent(event);
> +        ret = thread_NtDeviceIoControlFile(i, (HANDLE)listener, tests_no_port[i].event ? event : NULL,

Shouldn't "i" here be "tests_no_port[i].kill_thread"?

> +                tests_no_port[i].apc, tests_no_port[i].apc_context, &io,
> +                IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
> +        ok(ret == STATUS_PENDING, "got %#x\n", ret);
> +        ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status);
> +        if (tests_no_port[i].event)
> +        {
> +            ret = WaitForSingleObject(event, 1000);
> +            ok(!ret, "got %#x\n", ret);
> +        }
> +        winetest_pop_context();
> +    }
> +
> +    SleepEx(0, TRUE);
> +    ok(!test_async_thread_termination_apc_count, "got APC.\n");
> +
> +    port = CreateIoCompletionPort((HANDLE)listener, NULL, 0, 0);
> +
> +    for (i = 0; i < 2; ++i)
> +    {
> +        winetest_push_context("test %u", i);
> +        memset(&io, 0xcc, sizeof(io));
> +        ResetEvent(event);
> +        ret = thread_NtDeviceIoControlFile(i, (HANDLE)listener, event, NULL, (void *)0xdeadbeef, &io,
> +                IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
> +        ok(ret == STATUS_PENDING, "got %#x\n", ret);
> +        ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status);
> +        ret = WaitForSingleObject(event, 1000);
> +        ok(!ret, "got %#x\n", ret);
> +
> +        memset(&io, 0xcc, sizeof(io));
> +        key = 0xcc;
> +        value = 0;
> +        ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
> +        ok(!ret, "got %#x\n", ret);
> +        ok(!key, "got key %#Ix\n", key);
> +        ok(value == 0xdeadbeef, "got value %#Ix\n", value);
> +        ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status);
> +        winetest_pop_context();
> +    }
> +
> +    for (i = 0; i < 2; ++i)
> +    {
> +        winetest_push_context("test %u", i);
> +        memset(&io, 0xcc, sizeof(io));
> +        ResetEvent(event);
> +        ret = thread_NtDeviceIoControlFile(i, (HANDLE)listener, event, NULL, NULL, &io,
> +                IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
> +        ok(ret == STATUS_PENDING, "got %#x\n", ret);
> +        ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status);
> +
> +        ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
> +        ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
> +        winetest_pop_context();
> +    }
> +
> +    for (i = 0; i < 2; ++i)
> +    {
> +        winetest_push_context("test %u", i);
> +        memset(&io, 0xcc, sizeof(io));
> +        ret = thread_NtDeviceIoControlFile(i, (HANDLE)listener, NULL, NULL, NULL, &io,
> +                IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
> +        ok(ret == STATUS_PENDING, "got %#x\n", ret);
> +        ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status);
> +
> +        ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
> +        ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
> +        winetest_pop_context();
> +    }
> +
> +    /* async is not cancelled if there is a completion port and no event. */
> +    for (i = 0; i < 2; ++i)
> +    {
> +        winetest_push_context("test %u", i);
> +        memset(&io, 0xcc, sizeof(io));
> +        ret = thread_NtDeviceIoControlFile(i, (HANDLE)listener, NULL, NULL, (void *)0xdeadbeef, &io,
> +                IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
> +        ok(ret == STATUS_PENDING, "got %#x\n", ret);
> +        ok(io.Status == 0xcccccccc, "got %#lx\n", io.Status);
> +
> +        memset(&io, 0xcc, sizeof(io));
> +        key = 0xcc;
> +        value = 0;
> +        ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
> +        ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
> +        CancelIoEx((HANDLE)listener, NULL);
> +        ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
> +        ok(!ret, "got %#x\n", ret);
> +        ok(!key, "got key %#Ix\n", key);
> +        ok(value == 0xdeadbeef, "got value %#Ix\n", value);
> +        ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status);
> +        winetest_pop_context();
> +    }

FWIW, in cases like this I often find it preferable to iterate over the 
same loop but do something like

     if (tests[i].apc_context && !tests[i].event)
     {
         ok(io.Status == 0xcccccccc, "got %#lx\n", io.Status);
         /* other tests... */
     }
     else
     {
         ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status);
         /* other tests... */
     }

Just something to consider.



More information about the wine-devel mailing list