Closed matu3ba closed 6 months ago
From https://devblogs.microsoft.com/oldnewthing/20130405-00/?p=4743
#define [_UNICODE](http://blogs.msdn.com/b/oldnewthing/archive/2004/02/12/71851.aspx)
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <atlbase.h>
#include <atlalloc.h>
#include <shlwapi.h>
int __cdecl wmain(int argc, PWSTR argv[])
{
CHandle Job(CreateJobObject(nullptr, nullptr));
if (!Job) {
wprintf(L”CreateJobObject, error %d\n”, GetLastError());
return 0;
}
CHandle IOPort(CreateIoCompletionPort(INVALID_HANDLE_VALUE,
nullptr, 0, 1));
if (!IOPort) {
wprintf(L”CreateIoCompletionPort, error %d\n”,
GetLastError());
return 0;
}
JOBOBJECT_ASSOCIATE_COMPLETION_PORT Port;
Port.CompletionKey = Job;
Port.CompletionPort = IOPort;
if (!SetInformationJobObject(Job,
JobObjectAssociateCompletionPortInformation,
&Port, sizeof(Port))) {
wprintf(L”SetInformation, error %d\n”, GetLastError());
return 0;
}
PROCESS_INFORMATION ProcessInformation;
STARTUPINFO StartupInfo = { sizeof(StartupInfo) };
PWSTR [CommandLine](http://blogs.msdn.com/b/oldnewthing/archive/2011/08/15/10195600.aspx) = PathGetArgs(GetCommandLine());
if (!CreateProcess(nullptr, CommandLine, nullptr, nullptr,
FALSE, CREATE_SUSPENDED, nullptr, nullptr,
&StartupInfo, &ProcessInformation)) {
wprintf(L”CreateProcess, error %d\n”, GetLastError());
return 0;
}
if (!AssignProcessToJobObject(Job,
ProcessInformation.hProcess)) {
wprintf(L”Assign, error %d\n”, GetLastError());
return 0;
}
ResumeThread(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
DWORD CompletionCode;
ULONG_PTR CompletionKey;
LPOVERLAPPED Overlapped;
while (GetQueuedCompletionStatus(IOPort, &CompletionCode,
&CompletionKey, &Overlapped, INFINITE) &&
!((HANDLE)CompletionKey == Job &&
CompletionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)) {
wprintf(L”Still waiting…\n”);
}
wprintf(L”All done\n”);
return 0;
}
In depth explanation of non-scaling alternatives with some practice tips https://www.microsoftpressstore.com/articles/article.aspx?p=2224047&seqNum=5
Thread association by usage of I/O completion port https://devblogs.microsoft.com/oldnewthing/20210120-00/?p=104740
https://devblogs.microsoft.com/oldnewthing/20130405-00/?p=4743
https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_associate_completion_port?redirectedfrom=MSDN "Note that, except for limits set with the JobObjectNotificationLimitInformation information class, messages are intended only as notifications and their delivery to the completion port is not guaranteed. The failure of a message to arrive at the completion port does not necessarily mean that the event did not occur. Notifications for limits set with JobObjectNotificationLimitInformation are guaranteed to arrive at the completion port."
Afaiu, there is no guarantee that a notification queue is not full due to extensive I/O action and the notification JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO on the job object is lost. https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus
assumption: process events (termination etc) are too unlikely to get lost
documented for others and implemented. logic is evident from reading code meaning upper bound needed or executed trusted to not stall execution. closing.
See CI, which can be locally reproduced with a few attempts and/or under heavy system load:
Probably Windows has a dedicated method to wait for process group termination.