Skip to content

Commit b060339

Browse files
Refactor Process.open_files() to minimize threaded section
Signed-off-by: Jesse Schwartzentruber <[email protected]>
1 parent 2da9950 commit b060339

File tree

1 file changed

+108
-124
lines changed

1 file changed

+108
-124
lines changed

psutil/arch/windows/process_handles.c

Lines changed: 108 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,15 @@
2727

2828

2929
#define THREAD_TIMEOUT 100 // ms
30-
// Global object shared between the 2 threads.
31-
PUNICODE_STRING globalFileName = NULL;
30+
#define STATUS_UNSUCCESSFUL ((DWORD)0xC0000001L)
31+
32+
typedef struct
33+
{
34+
NTSTATUS status;
35+
HANDLE hFile;
36+
PUNICODE_STRING FileName;
37+
ULONG Length;
38+
} psutil_query_thread;
3239

3340

3441
static int
@@ -80,129 +87,105 @@ psutil_enum_handles(PSYSTEM_HANDLE_INFORMATION_EX *handles) {
8087
return 0;
8188
}
8289

83-
84-
static int
90+
static DWORD
8591
psutil_get_filename(LPVOID lpvParam) {
86-
HANDLE hFile = *((HANDLE*)lpvParam);
87-
NTSTATUS status;
88-
ULONG bufferSize;
89-
ULONG attempts = 8;
90-
91-
bufferSize = 0x200;
92-
globalFileName = MALLOC_ZERO(bufferSize);
93-
if (globalFileName == NULL) {
94-
PyErr_NoMemory();
95-
goto error;
96-
}
92+
psutil_query_thread* context = lpvParam;
93+
94+
context->status = NtQueryObject(
95+
context->hFile,
96+
ObjectNameInformation,
97+
context->FileName,
98+
context->Length,
99+
&context->Length);
100+
return 0;
101+
}
97102

98103

99-
// Note: also this is supposed to hang, hence why we do it in here.
100-
if (GetFileType(hFile) != FILE_TYPE_DISK) {
101-
SetLastError(0);
102-
globalFileName->Length = 0;
103-
return 0;
104+
static PUNICODE_STRING
105+
psutil_threaded_get_filename(HANDLE hFile) {
106+
DWORD dwWait;
107+
HANDLE hThread;
108+
DWORD result = 0;
109+
ULONG attempts = 8;
110+
psutil_query_thread threadContext = {
111+
.status = STATUS_UNSUCCESSFUL,
112+
.hFile = hFile,
113+
.FileName = NULL,
114+
.Length = 0x200,
115+
};
116+
117+
threadContext.FileName = MALLOC_ZERO(threadContext.Length);
118+
if (threadContext.FileName == NULL) {
119+
PyErr_NoMemory();
120+
return NULL;
104121
}
105122

106123
// A loop is needed because the I/O subsystem likes to give us the
107124
// wrong return lengths...
108125
do {
109-
status = NtQueryObject(
110-
hFile,
111-
ObjectNameInformation,
112-
globalFileName,
113-
bufferSize,
114-
&bufferSize
115-
);
116-
if (status == STATUS_BUFFER_OVERFLOW ||
117-
status == STATUS_INFO_LENGTH_MISMATCH ||
118-
status == STATUS_BUFFER_TOO_SMALL)
126+
hThread = CreateThread(
127+
NULL, 0, (LPTHREAD_START_ROUTINE)psutil_get_filename,
128+
&threadContext, 0, NULL);
129+
if (hThread == NULL) {
130+
PyErr_SetFromOSErrnoWithSyscall("CreateThread");
131+
FREE(threadContext.FileName);
132+
return NULL;
133+
}
134+
135+
// Wait for the worker thread to finish.
136+
dwWait = WaitForSingleObject(hThread, THREAD_TIMEOUT);
137+
138+
// If the thread hangs, kill it and cleanup.
139+
if (dwWait == WAIT_TIMEOUT) {
140+
psutil_debug(
141+
"get handle name thread timed out after %i ms", THREAD_TIMEOUT);
142+
if (TerminateThread(hThread, 0) == 0) {
143+
PyErr_SetFromOSErrnoWithSyscall("TerminateThread");
144+
result = 1;
145+
}
146+
} else if (dwWait == WAIT_FAILED) {
147+
psutil_debug("WaitForSingleObject -> WAIT_FAILED");
148+
result = 1;
149+
if (TerminateThread(hThread, 0) == 0) {
150+
PyErr_SetFromOSErrnoWithSyscall(
151+
"WaitForSingleObject -> WAIT_FAILED -> TerminateThread");
152+
} else {
153+
PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject");
154+
}
155+
} else if (GetExitCodeThread(hThread, &result) == 0) {
156+
result = 1;
157+
PyErr_SetFromOSErrnoWithSyscall("GetExitCodeThread");
158+
} else if (threadContext.status == STATUS_BUFFER_OVERFLOW ||
159+
threadContext.status == STATUS_INFO_LENGTH_MISMATCH ||
160+
threadContext.status == STATUS_BUFFER_TOO_SMALL)
119161
{
120-
FREE(globalFileName);
121-
globalFileName = MALLOC_ZERO(bufferSize);
122-
if (globalFileName == NULL) {
162+
FREE(threadContext.FileName);
163+
threadContext.FileName = MALLOC_ZERO(threadContext.Length);
164+
if (threadContext.FileName == NULL) {
123165
PyErr_NoMemory();
124-
goto error;
166+
CloseHandle(hThread);
167+
return NULL;
125168
}
126-
}
127-
else {
169+
} else {
170+
CloseHandle(hThread);
128171
break;
129172
}
130-
} while (--attempts);
131-
132-
if (! NT_SUCCESS(status)) {
133-
psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
134-
FREE(globalFileName);
135-
globalFileName = NULL;
136-
return 1;
137-
}
138-
139-
return 0;
140-
141-
error:
142-
if (globalFileName != NULL) {
143-
FREE(globalFileName);
144-
globalFileName = NULL;
145-
}
146-
return 1;
147-
}
148-
149-
150-
static DWORD
151-
psutil_threaded_get_filename(HANDLE hFile) {
152-
DWORD dwWait;
153-
HANDLE hThread;
154-
DWORD threadRetValue;
155-
156-
hThread = CreateThread(
157-
NULL, 0, (LPTHREAD_START_ROUTINE)psutil_get_filename, &hFile, 0, NULL);
158-
if (hThread == NULL) {
159-
PyErr_SetFromOSErrnoWithSyscall("CreateThread");
160-
return 1;
161-
}
173+
CloseHandle(hThread);
162174

163-
// Wait for the worker thread to finish.
164-
dwWait = WaitForSingleObject(hThread, THREAD_TIMEOUT);
175+
} while (--attempts && !result);
165176

166-
// If the thread hangs, kill it and cleanup.
167-
if (dwWait == WAIT_TIMEOUT) {
168-
psutil_debug(
169-
"get handle name thread timed out after %i ms", THREAD_TIMEOUT);
170-
if (TerminateThread(hThread, 0) == 0) {
171-
PyErr_SetFromOSErrnoWithSyscall("TerminateThread");
172-
CloseHandle(hThread);
173-
return 1;
174-
}
175-
CloseHandle(hThread);
176-
return 0;
177+
if (result) {
178+
FREE(threadContext.FileName);
179+
return NULL;
177180
}
178181

179-
if (dwWait == WAIT_FAILED) {
180-
psutil_debug("WaitForSingleObject -> WAIT_FAILED");
181-
if (TerminateThread(hThread, 0) == 0) {
182-
PyErr_SetFromOSErrnoWithSyscall(
183-
"WaitForSingleObject -> WAIT_FAILED -> TerminateThread");
184-
CloseHandle(hThread);
185-
return 1;
186-
}
187-
PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject");
188-
CloseHandle(hThread);
189-
return 1;
182+
if (! NT_SUCCESS(threadContext.status)) {
183+
psutil_SetFromNTStatusErr(threadContext.status, "NtQuerySystemInformation");
184+
FREE(threadContext.FileName);
185+
return NULL;
190186
}
191187

192-
if (GetExitCodeThread(hThread, &threadRetValue) == 0) {
193-
if (TerminateThread(hThread, 0) == 0) {
194-
PyErr_SetFromOSErrnoWithSyscall(
195-
"GetExitCodeThread (failed) -> TerminateThread");
196-
CloseHandle(hThread);
197-
return 1;
198-
}
199-
200-
CloseHandle(hThread);
201-
PyErr_SetFromOSErrnoWithSyscall("GetExitCodeThread");
202-
return 1;
203-
}
204-
CloseHandle(hThread);
205-
return threadRetValue;
188+
return threadContext.FileName;
206189
}
207190

208191

@@ -214,15 +197,12 @@ psutil_get_open_files(DWORD dwPid, HANDLE hProcess) {
214197
ULONG i = 0;
215198
BOOLEAN errorOccurred = FALSE;
216199
PyObject* py_path = NULL;
217-
PyObject* py_retlist = PyList_New(0);;
200+
PyObject* py_retlist = PyList_New(0);
201+
PUNICODE_STRING fileName = NULL;
218202

219203
if (!py_retlist)
220204
return NULL;
221205

222-
// Due to the use of global variables, ensure only 1 call
223-
// to psutil_get_open_files() is running.
224-
EnterCriticalSection(&PSUTIL_CRITICAL_SECTION);
225-
226206
if (psutil_enum_handles(&handlesList) != 0)
227207
goto error;
228208

@@ -243,13 +223,20 @@ psutil_get_open_files(DWORD dwPid, HANDLE hProcess) {
243223
continue;
244224
}
245225

246-
// This will set *globalFileName* global variable.
247-
if (psutil_threaded_get_filename(hFile) != 0)
226+
if (GetFileType(hFile) != FILE_TYPE_DISK) {
227+
SetLastError(0);
228+
CloseHandle(hFile);
229+
hFile = NULL;
230+
continue;
231+
}
232+
233+
fileName = psutil_threaded_get_filename(hFile);
234+
if (fileName == NULL)
248235
goto error;
249236

250-
if ((globalFileName != NULL) && (globalFileName->Length > 0)) {
251-
py_path = PyUnicode_FromWideChar(globalFileName->Buffer,
252-
wcslen(globalFileName->Buffer));
237+
if ((fileName != NULL) && (fileName->Length > 0)) {
238+
py_path = PyUnicode_FromWideChar(fileName->Buffer,
239+
wcslen(fileName->Buffer));
253240
if (! py_path)
254241
goto error;
255242
if (PyList_Append(py_retlist, py_path))
@@ -258,9 +245,9 @@ psutil_get_open_files(DWORD dwPid, HANDLE hProcess) {
258245
}
259246

260247
// Loop cleanup section.
261-
if (globalFileName != NULL) {
262-
FREE(globalFileName);
263-
globalFileName = NULL;
248+
if (fileName != NULL) {
249+
FREE(fileName);
250+
fileName = NULL;
264251
}
265252
CloseHandle(hFile);
266253
hFile = NULL;
@@ -276,16 +263,13 @@ psutil_get_open_files(DWORD dwPid, HANDLE hProcess) {
276263
exit:
277264
if (hFile != NULL)
278265
CloseHandle(hFile);
279-
if (globalFileName != NULL) {
280-
FREE(globalFileName);
281-
globalFileName = NULL;
282-
}
266+
if (fileName != NULL)
267+
FREE(fileName);
283268
if (py_path != NULL)
284269
Py_DECREF(py_path);
285270
if (handlesList != NULL)
286271
FREE(handlesList);
287272

288-
LeaveCriticalSection(&PSUTIL_CRITICAL_SECTION);
289273
if (errorOccurred == TRUE)
290274
return NULL;
291275
return py_retlist;

0 commit comments

Comments
 (0)