<div dir="auto"><div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Apr 19, 2022, 2:49 PM Daniel Lehman <<a href="mailto:dlehman25@gmail.com">dlehman25@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Signed-off-by: Daniel Lehman <<a href="mailto:dlehman25@gmail.com" target="_blank" rel="noreferrer">dlehman25@gmail.com</a>><br>
---<br>
 dlls/ntdll/tests/pipe.c | 11 +----------<br>
 dlls/ntdll/unix/file.c  | 18 ++++++++++++++++--<br>
 server/async.c          | 33 +++++++++++++++++++++++++++++++++<br>
 server/protocol.def     |  4 ++++<br>
 4 files changed, 54 insertions(+), 12 deletions(-)<br>
<br>
diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c<br>
index 1298e7a37ae..cfad711a09d 100644<br>
--- a/dlls/ntdll/tests/pipe.c<br>
+++ b/dlls/ntdll/tests/pipe.c<br>
@@ -574,11 +574,9 @@ static DWORD WINAPI synchronousio_thread(void *arg)<br>
     U(iosb).Status = 0xdeadbabe;<br>
     iosb.Information = 0xdeadbeef;<br>
     res = listen_pipe(pipe, NULL, &iosb, FALSE);<br>
-    todo_wine {<br>
     ok(res == STATUS_CANCELLED, "NtFsControlFile returned %lx\n", res);<br>
     ok(U(iosb).Status == 0xdeadbabe, "wrong status %lx\n", U(iosb).Status);<br>
     ok(iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information);<br>
-    }<br>
     return 0;<br>
 }<br>
<br>
@@ -594,13 +592,10 @@ static void test_cancelsynchronousio(void)<br>
<br>
     /* bogus values */<br>
     res = pNtCancelSynchronousIoFile((HANDLE)0xdeadbeef, NULL, &iosb);<br>
-    todo_wine<br>
     ok(res == STATUS_INVALID_HANDLE, "NtCancelSynchronousIoFile returned %lx\n", res);<br>
     res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, NULL);<br>
-    todo_wine<br>
     ok(res == STATUS_ACCESS_VIOLATION, "NtCancelSynchronousIoFile returned %lx\n", res);<br>
     res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, (IO_STATUS_BLOCK*)0xdeadbeef);<br>
-    todo_wine<br>
     ok(res == STATUS_ACCESS_VIOLATION, "NtCancelSynchronousIoFile returned %lx\n", res);<br>
     memset(&iosb, 0x55, sizeof(iosb));<br>
     res = pNtCancelSynchronousIoFile(GetCurrentThread(), (HANDLE)0xdeadbeef, &iosb);<br>
@@ -616,14 +611,12 @@ static void test_cancelsynchronousio(void)<br>
     Sleep(100);<br>
     memset(&iosb, 0x55, sizeof(iosb));<br>
     res = pNtCancelSynchronousIoFile(thread, NULL, &iosb);<br>
-    todo_wine {<br>
     ok(res == STATUS_SUCCESS, "Failed to cancel I/O\n");<br>
     ok(U(iosb).Status == STATUS_SUCCESS, "iosb.Status got changed to %lx\n", U(iosb).Status);<br>
     ok(U(iosb).Information == 0, "iosb.Information got changed to %Iu\n", U(iosb).Information);<br>
-    }<br>
-    CloseHandle(pipe);<br>
     WaitForSingleObject(thread, INFINITE);<br>
     CloseHandle(thread);<br>
+    CloseHandle(pipe);<br>
<br>
     /* asynchronous i/o */<br>
     res = create_pipe(&pipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */);<br>
@@ -635,11 +628,9 @@ static void test_cancelsynchronousio(void)<br>
     ok(res == STATUS_PENDING, "NtFsControlFile returned %lx\n", res);<br>
     memset(&iosb, 0x55, sizeof(iosb));<br>
     res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, &iosb);<br>
-    todo_wine {<br>
     ok(res == STATUS_NOT_FOUND, "NtCancelSynchronousIoFile returned %lx\n", res);<br>
     ok(U(iosb).Status == STATUS_NOT_FOUND, "iosb.Status got changed to %lx\n", U(iosb).Status);<br>
     ok(U(iosb).Information == 0, "iosb.Information got changed to %Iu\n", U(iosb).Information);<br>
-    }<br>
     ret = WaitForSingleObject(event, 0);<br>
     ok(ret == WAIT_TIMEOUT, "wait returned %lx\n", ret);<br>
     client = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,<br>
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c<br>
index 6990f9b4719..d833a136357 100644<br>
--- a/dlls/ntdll/unix/file.c<br>
+++ b/dlls/ntdll/unix/file.c<br>
@@ -5975,8 +5975,22 @@ NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_<br>
  */<br>
 NTSTATUS WINAPI NtCancelSynchronousIoFile( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status )<br>
 {<br>
-    FIXME( "(%p,%p,%p) stub\n", handle, io, io_status );<br>
-    return STATUS_NOT_IMPLEMENTED;<br>
+    NTSTATUS status;<br>
+<br>
+    FIXME( "(%p %p %p),partial stub\n", handle, io, io_status );<br>
+<br>
+    if (io) FIXME( "stub: unused IO_STATUS_BLOCK\n" );<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">If io is specified, it should never cancel any I/O operations other than the one specified by the parameter.</div><div dir="auto"><br></div><div dir="auto">Better keep returning STATUS_NOT_IMPLEMENTED in this case?</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+    SERVER_START_REQ( cancel_sync )<br>
+    {<br>
+        req->handle = wine_server_obj_handle( handle );<br>
+        status = wine_server_call( req );<br>
+    }<br>
+    SERVER_END_REQ;<br>
+<br>
+    io_status->u.Status = status;<br>
+    io_status->Information = 0;<br>
+    return status;<br>
 }<br>
<br>
 /******************************************************************<br>
diff --git a/server/async.c b/server/async.c<br>
index a4fbeab555e..b78d3a0532a 100644<br>
--- a/server/async.c<br>
+++ b/server/async.c<br>
@@ -588,6 +588,26 @@ restart:<br>
     return woken;<br>
 }<br>
<br>
+static int cancel_blocking( struct process *process, struct thread *thread )<br>
+{<br>
+    struct async *async;<br>
+    int woken = 0;<br>
+<br>
+restart:<br>
+    LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry )<br>
+    {<br>
+        if (async->terminated || async->canceled) continue;<br>
+        if ((async->thread == thread) && async->blocking)<br>
+        {<br>
+            async->canceled = 1;<br>
+            fd_cancel_async( async->fd, async );<br>
+            woken++;<br>
+            goto restart;<br>
+        }<br>
+    }<br>
+    return woken;<br>
+}<br>
+<br>
 void cancel_process_asyncs( struct process *process )<br>
 {<br>
     cancel_async( process, NULL, NULL, 0 );<br>
@@ -715,6 +735,19 @@ struct async *find_pending_async( struct async_queue *queue )<br>
     return NULL;<br>
 }<br>
<br>
+/* cancels sync I/O on a thread */<br>
+DECL_HANDLER(cancel_sync)<br>
+{<br>
+    struct thread *thread = get_thread_from_handle( req->handle, THREAD_TERMINATE );<br>
+<br>
+    if (thread)<br>
+    {<br>
+        if (!cancel_blocking( current->process, thread ))<br>
+            set_error( STATUS_NOT_FOUND );<br>
+        release_object( thread );<br>
+    }<br>
+}<br>
+<br>
 /* cancels all async I/O */<br>
 DECL_HANDLER(cancel_async)<br>
 {<br>
diff --git a/server/protocol.def b/server/protocol.def<br>
index 9b7b99ae86a..52a1b7767ce 100644<br>
--- a/server/protocol.def<br>
+++ b/server/protocol.def<br>
@@ -2135,6 +2135,10 @@ enum message_type<br>
 #define SERIALINFO_PENDING_WRITE 0x04<br>
 #define SERIALINFO_PENDING_WAIT  0x08<br>
<br>
+/* Cancel all sync io on a thread */<br>
+@REQ(cancel_sync)<br>
+    obj_handle_t handle;        /* thread handle on which to cancel io */<br>
+@END<br>
<br>
 /* Create an async I/O */<br>
 @REQ(register_async)<br>
-- <br>
2.25.1<br>
<br>
<br>
</blockquote></div></div></div>