Skip to content

Commit be1c808

Browse files
author
vxiiduu
authored
gh-116195: Implements a fast path for nt.getppid (GH-116205)
Use the NtQueryInformationProcess system call to efficiently retrieve the parent process ID in a single step, rather than using the process snapshots API which retrieves large amounts of unnecessary information and is more prone to failure (since it makes heap allocations). Includes a fallback to the original win32_getppid implementation in case the unstable API appears to return strange results.
1 parent 7bbb9b5 commit be1c808

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improves performance of :func:`os.getppid` by using an alternate system API when available. Contributed by vxiiduu.

Modules/posixmodule.c

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9115,16 +9115,98 @@ os_setpgrp_impl(PyObject *module)
91159115
#ifdef HAVE_GETPPID
91169116

91179117
#ifdef MS_WINDOWS
9118-
#include <processsnapshot.h>
9118+
#include <winternl.h>
9119+
#include <ProcessSnapshot.h>
9120+
9121+
// The structure definition in winternl.h may be incomplete.
9122+
// This structure is the full version from the MSDN documentation.
9123+
typedef struct _PROCESS_BASIC_INFORMATION_FULL {
9124+
NTSTATUS ExitStatus;
9125+
PVOID PebBaseAddress;
9126+
ULONG_PTR AffinityMask;
9127+
LONG BasePriority;
9128+
ULONG_PTR UniqueProcessId;
9129+
ULONG_PTR InheritedFromUniqueProcessId;
9130+
} PROCESS_BASIC_INFORMATION_FULL;
9131+
9132+
typedef NTSTATUS (NTAPI *PNT_QUERY_INFORMATION_PROCESS) (
9133+
IN HANDLE ProcessHandle,
9134+
IN PROCESSINFOCLASS ProcessInformationClass,
9135+
OUT PVOID ProcessInformation,
9136+
IN ULONG ProcessInformationLength,
9137+
OUT PULONG ReturnLength OPTIONAL);
9138+
9139+
// This function returns the process ID of the parent process.
9140+
// Returns 0 on failure.
9141+
static ULONG
9142+
win32_getppid_fast(void)
9143+
{
9144+
NTSTATUS status;
9145+
HMODULE ntdll;
9146+
PNT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess;
9147+
PROCESS_BASIC_INFORMATION_FULL basic_information;
9148+
static ULONG cached_ppid = 0;
9149+
9150+
if (cached_ppid) {
9151+
// No need to query the kernel again.
9152+
return cached_ppid;
9153+
}
9154+
9155+
ntdll = GetModuleHandleW(L"ntdll.dll");
9156+
if (!ntdll) {
9157+
return 0;
9158+
}
9159+
9160+
pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(ntdll, "NtQueryInformationProcess");
9161+
if (!pNtQueryInformationProcess) {
9162+
return 0;
9163+
}
9164+
9165+
status = pNtQueryInformationProcess(GetCurrentProcess(),
9166+
ProcessBasicInformation,
9167+
&basic_information,
9168+
sizeof(basic_information),
9169+
NULL);
9170+
9171+
if (!NT_SUCCESS(status)) {
9172+
return 0;
9173+
}
9174+
9175+
// Perform sanity check on the parent process ID we received from NtQueryInformationProcess.
9176+
// The check covers values which exceed the 32-bit range (if running on x64) as well as
9177+
// zero and (ULONG) -1.
9178+
9179+
if (basic_information.InheritedFromUniqueProcessId == 0 ||
9180+
basic_information.InheritedFromUniqueProcessId >= ULONG_MAX)
9181+
{
9182+
return 0;
9183+
}
9184+
9185+
// Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId
9186+
// structure member contains a ULONG_PTR which represents the process ID of our parent
9187+
// process. This process ID will be correctly returned even if the parent process has
9188+
// exited or been terminated.
9189+
9190+
cached_ppid = (ULONG) basic_information.InheritedFromUniqueProcessId;
9191+
return cached_ppid;
9192+
}
91199193

91209194
static PyObject*
91219195
win32_getppid(void)
91229196
{
91239197
DWORD error;
91249198
PyObject* result = NULL;
91259199
HANDLE process = GetCurrentProcess();
9126-
91279200
HPSS snapshot = NULL;
9201+
ULONG pid;
9202+
9203+
pid = win32_getppid_fast();
9204+
if (pid != 0) {
9205+
return PyLong_FromUnsignedLong(pid);
9206+
}
9207+
9208+
// If failure occurs in win32_getppid_fast(), fall back to using the PSS API.
9209+
91289210
error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot);
91299211
if (error != ERROR_SUCCESS) {
91309212
return PyErr_SetFromWindowsErr(error);

0 commit comments

Comments
 (0)