diff --git a/dlls/kernel32/sync.c b/dlls/kernel32/sync.c index 1bb2c8aaea7..975b914a024 100644 --- a/dlls/kernel32/sync.c +++ b/dlls/kernel32/sync.c @@ -1912,8 +1912,13 @@ BOOL WINAPI PostQueuedCompletionStatus( HANDLE CompletionPort, DWORD dwNumberOfB */ BOOL WINAPI BindIoCompletionCallback( HANDLE FileHandle, LPOVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags) { - FIXME("%p, %p, %d, stub!\n", FileHandle, Function, Flags); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + NTSTATUS status; + + TRACE("(%p, %p, %d)\n", FileHandle, Function, Flags); + + status = RtlSetIoCompletionCallback( FileHandle, (PRTL_OVERLAPPED_COMPLETION_ROUTINE)Function, Flags ); + if (status == STATUS_SUCCESS) return TRUE; + SetLastError( RtlNtStatusToDosError(status) ); return FALSE; } diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c index e402fede9a5..3ab86eab13f 100644 --- a/dlls/kernel32/tests/sync.c +++ b/dlls/kernel32/tests/sync.c @@ -326,10 +326,6 @@ static void test_iocp_callback(void) retb = p_BindIoCompletionCallback(hFile, iocp_callback, 0); ok(retb == FALSE, "BindIoCompletionCallback succeeded on a file that wasn't created with FILE_FLAG_OVERLAPPED\n"); - if(retb == FALSE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { - todo_wine ok (0, "BindIoCompletionCallback returned ERROR_CALL_NOT_IMPLEMENTED\n"); - return; - } ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error is %d\n", GetLastError()); ret = CloseHandle(hFile); diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index fc1a6c8070e..1a7db963bbb 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -822,7 +822,7 @@ @ stdcall RtlSetGroupSecurityDescriptor(ptr ptr long) # @ stub RtlSetHeapInformation @ stub RtlSetInformationAcl -# @ stub RtlSetIoCompletionCallback +@ stdcall RtlSetIoCompletionCallback(long ptr long) @ stdcall RtlSetLastWin32Error(long) @ stdcall RtlSetLastWin32ErrorAndNtStatusFromNtStatus(long) # @ stub RtlSetMemoryStreamSize diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 72ea0cf9be1..147667b0895 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -54,6 +54,16 @@ static RTL_CRITICAL_SECTION_DEBUG critsect_debug = }; static RTL_CRITICAL_SECTION threadpool_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; +static HANDLE compl_port = NULL; +static RTL_CRITICAL_SECTION threadpool_compl_cs; +static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug = +{ + 0, 0, &threadpool_compl_cs, + { &critsect_compl_debug.ProcessLocksList, &critsect_compl_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_compl_cs") } +}; +static RTL_CRITICAL_SECTION threadpool_compl_cs = { &critsect_compl_debug, -1, 0, 0, 0, 0 }; + struct work_item { struct list entry; @@ -218,3 +228,86 @@ NTSTATUS WINAPI RtlQueueWorkItem(PRTL_WORK_ITEM_ROUTINE Function, PVOID Context, return STATUS_SUCCESS; } + +/*********************************************************************** + * iocp_poller - get completion events and run callbacks + */ +static DWORD CALLBACK iocp_poller(LPVOID Arg) +{ + while( TRUE ) + { + PRTL_OVERLAPPED_COMPLETION_ROUTINE callback; + LPVOID overlapped; + IO_STATUS_BLOCK iosb; + NTSTATUS res = NtRemoveIoCompletion( compl_port, (PULONG_PTR)&callback, (PULONG_PTR)&overlapped, &iosb, NULL ); + if (res) + { + ERR("NtRemoveIoCompletion failed: 0x%x\n", res); + } + else + { + DWORD transferred = 0; + DWORD err = 0; + + if (iosb.u.Status == STATUS_SUCCESS) + transferred = iosb.Information; + else + err = RtlNtStatusToDosError(iosb.u.Status); + + callback( err, transferred, overlapped ); + } + } +} + +/*********************************************************************** + * RtlSetIoCompletionCallback (NTDLL.@) + * + * Binds a handle to a thread pool's completion port, and possibly + * starts a non-I/O thread to monitor this port and call functions back. + * + * PARAMS + * FileHandle [I] Handle to bind to a completion port. + * Function [I] Callback function to call on I/O completions. + * Flags [I] Not used. + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any NTSTATUS code. + * + */ +NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE FileHandle, PRTL_OVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags) +{ + IO_STATUS_BLOCK iosb; + FILE_COMPLETION_INFORMATION info; + + if (Flags) FIXME("Unknown value Flags=0x%x\n", Flags); + + if (!compl_port) + { + NTSTATUS res = STATUS_SUCCESS; + + RtlEnterCriticalSection(&threadpool_compl_cs); + if (!compl_port) + { + HANDLE cport; + + res = NtCreateIoCompletion( &cport, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); + if (!res) + { + /* FIXME native can start additional threads in case of e.g. hung callback function. */ + res = RtlQueueWorkItem( iocp_poller, NULL, WT_EXECUTEDEFAULT ); + if (!res) + compl_port = cport; + else + NtClose( cport ); + } + } + RtlLeaveCriticalSection(&threadpool_compl_cs); + if (res) return res; + } + + info.CompletionPort = compl_port; + info.CompletionKey = (ULONG_PTR)Function; + + return NtSetInformationFile( FileHandle, &iosb, &info, sizeof(info), FileCompletionInformation ); +} diff --git a/include/winternl.h b/include/winternl.h index 7ba6e5be1d4..79f31841398 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1568,6 +1568,8 @@ typedef struct _KEY_MULTIPLE_VALUE_INFORMATION ULONG Type; } KEY_MULTIPLE_VALUE_INFORMATION, *PKEY_MULTIPLE_VALUE_INFORMATION; +typedef VOID (CALLBACK *PRTL_OVERLAPPED_COMPLETION_ROUTINE)(DWORD,DWORD,LPVOID); + typedef VOID (*PTIMER_APC_ROUTINE) ( PVOID, ULONG, LONG ); typedef enum _EVENT_TYPE { @@ -2273,6 +2275,7 @@ NTSYSAPI NTSTATUS WINAPI RtlSetDaclSecurityDescriptor(PSECURITY_DESCRIPTOR,BOOL NTSYSAPI NTSTATUS WINAPI RtlSetEnvironmentVariable(PWSTR*,PUNICODE_STRING,PUNICODE_STRING); NTSYSAPI NTSTATUS WINAPI RtlSetOwnerSecurityDescriptor(PSECURITY_DESCRIPTOR,PSID,BOOLEAN); NTSYSAPI NTSTATUS WINAPI RtlSetGroupSecurityDescriptor(PSECURITY_DESCRIPTOR,PSID,BOOLEAN); +NTSYSAPI NTSTATUS WINAPI RtlSetIoCompletionCallback(HANDLE,PRTL_OVERLAPPED_COMPLETION_ROUTINE,ULONG); NTSYSAPI void WINAPI RtlSetLastWin32Error(DWORD); NTSYSAPI void WINAPI RtlSetLastWin32ErrorAndNtStatusFromNtStatus(NTSTATUS); NTSYSAPI NTSTATUS WINAPI RtlSetSaclSecurityDescriptor(PSECURITY_DESCRIPTOR,BOOLEAN,PACL,BOOLEAN);