gh-89240: Enable multiprocessing on Windows to use large process pools (GH-107873)

We add _winapi.BatchedWaitForMultipleObjects to wait for larger numbers of handles.
This is an internal module, hence undocumented, and should be used with caution.
Check the docstring for info before using BatchedWaitForMultipleObjects.
This commit is contained in:
Steve Dower 2024-02-13 00:28:35 +00:00 committed by GitHub
parent 2f0778675a
commit ea25f32d5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1195 additions and 6 deletions

View File

@ -883,6 +883,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(defaultaction));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(delete));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(depth));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(desired_access));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(detect_types));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(deterministic));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(device));
@ -973,6 +974,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(groups));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(h));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(handle));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(handle_seq));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hash_name));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(header));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(headers));
@ -990,9 +992,12 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(indexgroup));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(inf));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(infer_variance));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(inherit_handle));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(inheritable));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(initial));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(initial_bytes));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(initial_owner));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(initial_state));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(initial_value));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(initval));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(inner_size));
@ -1048,6 +1053,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(locals));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(logoption));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(loop));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(manual_reset));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mapping));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(match));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(max_length));
@ -1064,6 +1070,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(metadata));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(method));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(microsecond));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(milliseconds));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(minute));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mod));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mode));
@ -1073,6 +1080,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(month));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mro));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(msg));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mutex));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mycmp));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(n));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(n_arg));
@ -1176,6 +1184,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sched_priority));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(scheduler));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(second));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(security_attributes));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seek));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seekable));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(selectors));
@ -1263,6 +1272,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(values));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(version));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(volume));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(wait_all));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(warnings));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(warnoptions));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(wbits));

View File

@ -372,6 +372,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(defaultaction)
STRUCT_FOR_ID(delete)
STRUCT_FOR_ID(depth)
STRUCT_FOR_ID(desired_access)
STRUCT_FOR_ID(detect_types)
STRUCT_FOR_ID(deterministic)
STRUCT_FOR_ID(device)
@ -462,6 +463,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(groups)
STRUCT_FOR_ID(h)
STRUCT_FOR_ID(handle)
STRUCT_FOR_ID(handle_seq)
STRUCT_FOR_ID(hash_name)
STRUCT_FOR_ID(header)
STRUCT_FOR_ID(headers)
@ -479,9 +481,12 @@ struct _Py_global_strings {
STRUCT_FOR_ID(indexgroup)
STRUCT_FOR_ID(inf)
STRUCT_FOR_ID(infer_variance)
STRUCT_FOR_ID(inherit_handle)
STRUCT_FOR_ID(inheritable)
STRUCT_FOR_ID(initial)
STRUCT_FOR_ID(initial_bytes)
STRUCT_FOR_ID(initial_owner)
STRUCT_FOR_ID(initial_state)
STRUCT_FOR_ID(initial_value)
STRUCT_FOR_ID(initval)
STRUCT_FOR_ID(inner_size)
@ -537,6 +542,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(locals)
STRUCT_FOR_ID(logoption)
STRUCT_FOR_ID(loop)
STRUCT_FOR_ID(manual_reset)
STRUCT_FOR_ID(mapping)
STRUCT_FOR_ID(match)
STRUCT_FOR_ID(max_length)
@ -553,6 +559,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(metadata)
STRUCT_FOR_ID(method)
STRUCT_FOR_ID(microsecond)
STRUCT_FOR_ID(milliseconds)
STRUCT_FOR_ID(minute)
STRUCT_FOR_ID(mod)
STRUCT_FOR_ID(mode)
@ -562,6 +569,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(month)
STRUCT_FOR_ID(mro)
STRUCT_FOR_ID(msg)
STRUCT_FOR_ID(mutex)
STRUCT_FOR_ID(mycmp)
STRUCT_FOR_ID(n)
STRUCT_FOR_ID(n_arg)
@ -665,6 +673,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(sched_priority)
STRUCT_FOR_ID(scheduler)
STRUCT_FOR_ID(second)
STRUCT_FOR_ID(security_attributes)
STRUCT_FOR_ID(seek)
STRUCT_FOR_ID(seekable)
STRUCT_FOR_ID(selectors)
@ -752,6 +761,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(values)
STRUCT_FOR_ID(version)
STRUCT_FOR_ID(volume)
STRUCT_FOR_ID(wait_all)
STRUCT_FOR_ID(warnings)
STRUCT_FOR_ID(warnoptions)
STRUCT_FOR_ID(wbits)

View File

@ -881,6 +881,7 @@ extern "C" {
INIT_ID(defaultaction), \
INIT_ID(delete), \
INIT_ID(depth), \
INIT_ID(desired_access), \
INIT_ID(detect_types), \
INIT_ID(deterministic), \
INIT_ID(device), \
@ -971,6 +972,7 @@ extern "C" {
INIT_ID(groups), \
INIT_ID(h), \
INIT_ID(handle), \
INIT_ID(handle_seq), \
INIT_ID(hash_name), \
INIT_ID(header), \
INIT_ID(headers), \
@ -988,9 +990,12 @@ extern "C" {
INIT_ID(indexgroup), \
INIT_ID(inf), \
INIT_ID(infer_variance), \
INIT_ID(inherit_handle), \
INIT_ID(inheritable), \
INIT_ID(initial), \
INIT_ID(initial_bytes), \
INIT_ID(initial_owner), \
INIT_ID(initial_state), \
INIT_ID(initial_value), \
INIT_ID(initval), \
INIT_ID(inner_size), \
@ -1046,6 +1051,7 @@ extern "C" {
INIT_ID(locals), \
INIT_ID(logoption), \
INIT_ID(loop), \
INIT_ID(manual_reset), \
INIT_ID(mapping), \
INIT_ID(match), \
INIT_ID(max_length), \
@ -1062,6 +1068,7 @@ extern "C" {
INIT_ID(metadata), \
INIT_ID(method), \
INIT_ID(microsecond), \
INIT_ID(milliseconds), \
INIT_ID(minute), \
INIT_ID(mod), \
INIT_ID(mode), \
@ -1071,6 +1078,7 @@ extern "C" {
INIT_ID(month), \
INIT_ID(mro), \
INIT_ID(msg), \
INIT_ID(mutex), \
INIT_ID(mycmp), \
INIT_ID(n), \
INIT_ID(n_arg), \
@ -1174,6 +1182,7 @@ extern "C" {
INIT_ID(sched_priority), \
INIT_ID(scheduler), \
INIT_ID(second), \
INIT_ID(security_attributes), \
INIT_ID(seek), \
INIT_ID(seekable), \
INIT_ID(selectors), \
@ -1261,6 +1270,7 @@ extern "C" {
INIT_ID(values), \
INIT_ID(version), \
INIT_ID(volume), \
INIT_ID(wait_all), \
INIT_ID(warnings), \
INIT_ID(warnoptions), \
INIT_ID(wbits), \

View File

@ -957,6 +957,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(depth);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(desired_access);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(detect_types);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -1227,6 +1230,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(handle);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(handle_seq);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(hash_name);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -1278,6 +1284,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(infer_variance);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(inherit_handle);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(inheritable);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -1287,6 +1296,12 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(initial_bytes);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(initial_owner);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(initial_state);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(initial_value);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -1452,6 +1467,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(loop);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(manual_reset);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(mapping);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -1500,6 +1518,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(microsecond);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(milliseconds);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(minute);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -1527,6 +1548,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(msg);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(mutex);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(mycmp);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -1836,6 +1860,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(second);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(security_attributes);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(seek);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@ -2097,6 +2124,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(volume);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(wait_all);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(warnings);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);

View File

@ -1011,8 +1011,20 @@ if sys.platform == 'win32':
# returning the first signalled might create starvation issues.)
L = list(handles)
ready = []
# Windows limits WaitForMultipleObjects at 64 handles, and we use a
# few for synchronisation, so we switch to batched waits at 60.
if len(L) > 60:
try:
res = _winapi.BatchedWaitForMultipleObjects(L, False, timeout)
except TimeoutError:
return []
ready.extend(L[i] for i in res)
if res:
L = [h for i, h in enumerate(L) if i > res[0] & i not in res]
timeout = 0
while L:
res = _winapi.WaitForMultipleObjects(L, False, timeout)
short_L = L[:60] if len(L) > 60 else L
res = _winapi.WaitForMultipleObjects(short_L, False, timeout)
if res == WAIT_TIMEOUT:
break
elif WAIT_OBJECT_0 <= res < WAIT_OBJECT_0 + len(L):

View File

@ -6113,6 +6113,24 @@ class MiscTestCase(unittest.TestCase):
self.assertEqual(rc, 0)
self.assertFalse(err, msg=err.decode('utf-8'))
def test_large_pool(self):
#
# gh-89240: Check that large pools are always okay
#
testfn = os_helper.TESTFN
self.addCleanup(os_helper.unlink, testfn)
with open(testfn, 'w', encoding='utf-8') as f:
f.write(textwrap.dedent('''\
import multiprocessing
def f(x): return x*x
if __name__ == '__main__':
with multiprocessing.Pool(200) as p:
print(sum(p.map(f, range(1000))))
'''))
rc, out, err = script_helper.assert_python_ok(testfn)
self.assertEqual("332833500", out.decode('utf-8').strip())
self.assertFalse(err, msg=err.decode('utf-8'))
#
# Mixins

94
Lib/test/test_winapi.py Normal file
View File

@ -0,0 +1,94 @@
# Test the Windows-only _winapi module
import random
import threading
import time
import unittest
from test.support import import_helper
_winapi = import_helper.import_module('_winapi', required_on=['win'])
MAXIMUM_WAIT_OBJECTS = 64
MAXIMUM_BATCHED_WAIT_OBJECTS = (MAXIMUM_WAIT_OBJECTS - 1) ** 2
class WinAPIBatchedWaitForMultipleObjectsTests(unittest.TestCase):
def _events_waitall_test(self, n):
evts = [_winapi.CreateEventW(0, False, False, None) for _ in range(n)]
with self.assertRaises(TimeoutError):
_winapi.BatchedWaitForMultipleObjects(evts, True, 100)
# Ensure no errors raised when all are triggered
for e in evts:
_winapi.SetEvent(e)
try:
_winapi.BatchedWaitForMultipleObjects(evts, True, 100)
except TimeoutError:
self.fail("expected wait to complete immediately")
# Choose 8 events to set, distributed throughout the list, to make sure
# we don't always have them in the first chunk
chosen = [i * (len(evts) // 8) for i in range(8)]
# Replace events with invalid handles to make sure we fail
for i in chosen:
old_evt = evts[i]
evts[i] = -1
with self.assertRaises(OSError):
_winapi.BatchedWaitForMultipleObjects(evts, True, 100)
evts[i] = old_evt
def _events_waitany_test(self, n):
evts = [_winapi.CreateEventW(0, False, False, None) for _ in range(n)]
with self.assertRaises(TimeoutError):
_winapi.BatchedWaitForMultipleObjects(evts, False, 100)
# Choose 8 events to set, distributed throughout the list, to make sure
# we don't always have them in the first chunk
chosen = [i * (len(evts) // 8) for i in range(8)]
# Trigger one by one. They are auto-reset events, so will only trigger once
for i in chosen:
with self.subTest(f"trigger event {i} of {len(evts)}"):
_winapi.SetEvent(evts[i])
triggered = _winapi.BatchedWaitForMultipleObjects(evts, False, 10000)
self.assertSetEqual(set(triggered), {i})
# Trigger all at once. This may require multiple calls
for i in chosen:
_winapi.SetEvent(evts[i])
triggered = set()
while len(triggered) < len(chosen):
triggered.update(_winapi.BatchedWaitForMultipleObjects(evts, False, 10000))
self.assertSetEqual(triggered, set(chosen))
# Replace events with invalid handles to make sure we fail
for i in chosen:
with self.subTest(f"corrupt event {i} of {len(evts)}"):
old_evt = evts[i]
evts[i] = -1
with self.assertRaises(OSError):
_winapi.BatchedWaitForMultipleObjects(evts, False, 100)
evts[i] = old_evt
def test_few_events_waitall(self):
self._events_waitall_test(16)
def test_many_events_waitall(self):
self._events_waitall_test(256)
def test_max_events_waitall(self):
self._events_waitall_test(MAXIMUM_BATCHED_WAIT_OBJECTS)
def test_few_events_waitany(self):
self._events_waitany_test(16)
def test_many_events_waitany(self):
self._events_waitany_test(256)
def test_max_events_waitany(self):
self._events_waitany_test(MAXIMUM_BATCHED_WAIT_OBJECTS)

View File

@ -0,0 +1 @@
Allows :mod:`multiprocessing` to create pools of greater than 62 processes.

View File

@ -438,6 +438,39 @@ _winapi_ConnectNamedPipe_impl(PyObject *module, HANDLE handle,
Py_RETURN_NONE;
}
/*[clinic input]
_winapi.CreateEventW -> HANDLE
security_attributes: LPSECURITY_ATTRIBUTES
manual_reset: BOOL
initial_state: BOOL
name: LPCWSTR(accept={str, NoneType})
[clinic start generated code]*/
static HANDLE
_winapi_CreateEventW_impl(PyObject *module,
LPSECURITY_ATTRIBUTES security_attributes,
BOOL manual_reset, BOOL initial_state,
LPCWSTR name)
/*[clinic end generated code: output=2d4c7d5852ecb298 input=4187cee28ac763f8]*/
{
HANDLE handle;
if (PySys_Audit("_winapi.CreateEventW", "bbu", manual_reset, initial_state, name) < 0) {
return INVALID_HANDLE_VALUE;
}
Py_BEGIN_ALLOW_THREADS
handle = CreateEventW(security_attributes, manual_reset, initial_state, name);
Py_END_ALLOW_THREADS
if (handle == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErr(0);
}
return handle;
}
/*[clinic input]
_winapi.CreateFile -> HANDLE
@ -674,6 +707,37 @@ cleanup:
Py_RETURN_NONE;
}
/*[clinic input]
_winapi.CreateMutexW -> HANDLE
security_attributes: LPSECURITY_ATTRIBUTES
initial_owner: BOOL
name: LPCWSTR(accept={str, NoneType})
[clinic start generated code]*/
static HANDLE
_winapi_CreateMutexW_impl(PyObject *module,
LPSECURITY_ATTRIBUTES security_attributes,
BOOL initial_owner, LPCWSTR name)
/*[clinic end generated code: output=31b9ee8fc37e49a5 input=7d54b921e723254a]*/
{
HANDLE handle;
if (PySys_Audit("_winapi.CreateMutexW", "bu", initial_owner, name) < 0) {
return INVALID_HANDLE_VALUE;
}
Py_BEGIN_ALLOW_THREADS
handle = CreateMutexW(security_attributes, initial_owner, name);
Py_END_ALLOW_THREADS
if (handle == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErr(0);
}
return handle;
}
/*[clinic input]
_winapi.CreateNamedPipe -> HANDLE
@ -1590,6 +1654,67 @@ _winapi_UnmapViewOfFile_impl(PyObject *module, LPCVOID address)
Py_RETURN_NONE;
}
/*[clinic input]
_winapi.OpenEventW -> HANDLE
desired_access: DWORD
inherit_handle: BOOL
name: LPCWSTR
[clinic start generated code]*/
static HANDLE
_winapi_OpenEventW_impl(PyObject *module, DWORD desired_access,
BOOL inherit_handle, LPCWSTR name)
/*[clinic end generated code: output=c4a45e95545a4bd2 input=dec26598748d35aa]*/
{
HANDLE handle;
if (PySys_Audit("_winapi.OpenEventW", "Iu", desired_access, name) < 0) {
return INVALID_HANDLE_VALUE;
}
Py_BEGIN_ALLOW_THREADS
handle = OpenEventW(desired_access, inherit_handle, name);
Py_END_ALLOW_THREADS
if (handle == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErr(0);
}
return handle;
}
/*[clinic input]
_winapi.OpenMutexW -> HANDLE
desired_access: DWORD
inherit_handle: BOOL
name: LPCWSTR
[clinic start generated code]*/
static HANDLE
_winapi_OpenMutexW_impl(PyObject *module, DWORD desired_access,
BOOL inherit_handle, LPCWSTR name)
/*[clinic end generated code: output=dda39d7844397bf0 input=f3a7b466c5307712]*/
{
HANDLE handle;
if (PySys_Audit("_winapi.OpenMutexW", "Iu", desired_access, name) < 0) {
return INVALID_HANDLE_VALUE;
}
Py_BEGIN_ALLOW_THREADS
handle = OpenMutexW(desired_access, inherit_handle, name);
Py_END_ALLOW_THREADS
if (handle == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErr(0);
}
return handle;
}
/*[clinic input]
_winapi.OpenFileMapping -> HANDLE
@ -1820,6 +1945,75 @@ _winapi_ReadFile_impl(PyObject *module, HANDLE handle, DWORD size,
return Py_BuildValue("NI", buf, err);
}
/*[clinic input]
_winapi.ReleaseMutex
mutex: HANDLE
[clinic start generated code]*/
static PyObject *
_winapi_ReleaseMutex_impl(PyObject *module, HANDLE mutex)
/*[clinic end generated code: output=5b9001a72dd8af37 input=49e9d20de3559d84]*/
{
int err = 0;
Py_BEGIN_ALLOW_THREADS
if (!ReleaseMutex(mutex)) {
err = GetLastError();
}
Py_END_ALLOW_THREADS
if (err) {
return PyErr_SetFromWindowsErr(err);
}
Py_RETURN_NONE;
}
/*[clinic input]
_winapi.ResetEvent
event: HANDLE
[clinic start generated code]*/
static PyObject *
_winapi_ResetEvent_impl(PyObject *module, HANDLE event)
/*[clinic end generated code: output=81c8501d57c0530d input=e2d42d990322e87a]*/
{
int err = 0;
Py_BEGIN_ALLOW_THREADS
if (!ResetEvent(event)) {
err = GetLastError();
}
Py_END_ALLOW_THREADS
if (err) {
return PyErr_SetFromWindowsErr(err);
}
Py_RETURN_NONE;
}
/*[clinic input]
_winapi.SetEvent
event: HANDLE
[clinic start generated code]*/
static PyObject *
_winapi_SetEvent_impl(PyObject *module, HANDLE event)
/*[clinic end generated code: output=c18ba09eb9aa774d input=e660e830a37c09f8]*/
{
int err = 0;
Py_BEGIN_ALLOW_THREADS
if (!SetEvent(event)) {
err = GetLastError();
}
Py_END_ALLOW_THREADS
if (err) {
return PyErr_SetFromWindowsErr(err);
}
Py_RETURN_NONE;
}
/*[clinic input]
_winapi.SetNamedPipeHandleState
@ -1942,6 +2136,310 @@ _winapi_WaitNamedPipe_impl(PyObject *module, LPCTSTR name, DWORD timeout)
Py_RETURN_NONE;
}
typedef struct {
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
HANDLE cancel_event;
DWORD handle_base;
DWORD handle_count;
HANDLE thread;
volatile DWORD result;
} BatchedWaitData;
static DWORD WINAPI
_batched_WaitForMultipleObjects_thread(LPVOID param)
{
BatchedWaitData *data = (BatchedWaitData *)param;
data->result = WaitForMultipleObjects(
data->handle_count,
data->handles,
FALSE,
INFINITE
);
if (data->result == WAIT_FAILED) {
DWORD err = GetLastError();
SetEvent(data->cancel_event);
return err;
} else if (data->result >= WAIT_ABANDONED_0 && data->result < WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS) {
data->result = WAIT_FAILED;
SetEvent(data->cancel_event);
return ERROR_ABANDONED_WAIT_0;
}
return 0;
}
/*[clinic input]
_winapi.BatchedWaitForMultipleObjects
handle_seq: object
wait_all: BOOL
milliseconds: DWORD(c_default='INFINITE') = _winapi.INFINITE
Supports a larger number of handles than WaitForMultipleObjects
Note that the handles may be waited on other threads, which could cause
issues for objects like mutexes that become associated with the thread
that was waiting for them. Objects may also be left signalled, even if
the wait fails.
It is recommended to use WaitForMultipleObjects whenever possible, and
only switch to BatchedWaitForMultipleObjects for scenarios where you
control all the handles involved, such as your own thread pool or
files, and all wait objects are left unmodified by a wait (for example,
manual reset events, threads, and files/pipes).
Overlapped handles returned from this module use manual reset events.
[clinic start generated code]*/
static PyObject *
_winapi_BatchedWaitForMultipleObjects_impl(PyObject *module,
PyObject *handle_seq,
BOOL wait_all, DWORD milliseconds)
/*[clinic end generated code: output=d21c1a4ad0a252fd input=7e196f29005dc77b]*/
{
Py_ssize_t thread_count = 0, handle_count = 0, i, j;
Py_ssize_t nhandles;
BatchedWaitData *thread_data[MAXIMUM_WAIT_OBJECTS];
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
HANDLE sigint_event = NULL;
HANDLE cancel_event = NULL;
DWORD result;
const Py_ssize_t _MAXIMUM_TOTAL_OBJECTS = (MAXIMUM_WAIT_OBJECTS - 1) * (MAXIMUM_WAIT_OBJECTS - 1);
if (!PySequence_Check(handle_seq)) {
PyErr_Format(PyExc_TypeError,
"sequence type expected, got '%s'",
Py_TYPE(handle_seq)->tp_name);
return NULL;
}
nhandles = PySequence_Length(handle_seq);
if (nhandles == -1) {
return NULL;
}
if (nhandles == 0) {
return wait_all ? Py_NewRef(Py_None) : PyList_New(0);
}
/* If this is the main thread then make the wait interruptible
by Ctrl-C. When waiting for *all* handles, it is only checked
in between batches. */
if (_PyOS_IsMainThread()) {
sigint_event = _PyOS_SigintEvent();
assert(sigint_event != NULL);
}
if (nhandles < 0 || nhandles > _MAXIMUM_TOTAL_OBJECTS) {
PyErr_Format(PyExc_ValueError,
"need at most %zd handles, got a sequence of length %zd",
_MAXIMUM_TOTAL_OBJECTS, nhandles);
return NULL;
}
if (!wait_all) {
cancel_event = CreateEventW(NULL, TRUE, FALSE, NULL);
if (!cancel_event) {
PyErr_SetExcFromWindowsErr(PyExc_OSError, 0);
return NULL;
}
}
i = 0;
while (i < nhandles) {
BatchedWaitData *data = (BatchedWaitData*)PyMem_Malloc(sizeof(BatchedWaitData));
if (!data) {
goto error;
}
thread_data[thread_count++] = data;
data->thread = NULL;
data->cancel_event = cancel_event;
data->handle_base = Py_SAFE_DOWNCAST(i, Py_ssize_t, DWORD);
data->handle_count = Py_SAFE_DOWNCAST(nhandles - i, Py_ssize_t, DWORD);
if (data->handle_count > MAXIMUM_WAIT_OBJECTS - 1) {
data->handle_count = MAXIMUM_WAIT_OBJECTS - 1;
}
for (j = 0; j < data->handle_count; ++i, ++j) {
PyObject *v = PySequence_GetItem(handle_seq, i);
if (!v || !PyArg_Parse(v, F_HANDLE, &data->handles[j])) {
Py_XDECREF(v);
goto error;
}
Py_DECREF(v);
}
if (!wait_all) {
data->handles[data->handle_count++] = cancel_event;
}
}
DWORD err = 0;
/* We need to use different strategies when waiting for ALL handles
as opposed to ANY handle. This is because there is no way to
(safely) interrupt a thread that is waiting for all handles in a
group. So for ALL handles, we loop over each set and wait. For
ANY handle, we use threads and wait on them. */
if (wait_all) {
Py_BEGIN_ALLOW_THREADS
long long deadline = 0;
if (milliseconds != INFINITE) {
deadline = (long long)GetTickCount64() + milliseconds;
}
for (i = 0; !err && i < thread_count; ++i) {
DWORD timeout = milliseconds;
if (deadline) {
long long time_to_deadline = deadline - GetTickCount64();
if (time_to_deadline <= 0) {
err = WAIT_TIMEOUT;
break;
} else if (time_to_deadline < UINT_MAX) {
timeout = (DWORD)time_to_deadline;
}
}
result = WaitForMultipleObjects(thread_data[i]->handle_count,
thread_data[i]->handles, TRUE, timeout);
// ABANDONED is not possible here because we own all the handles
if (result == WAIT_FAILED) {
err = GetLastError();
} else if (result == WAIT_TIMEOUT) {
err = WAIT_TIMEOUT;
}
if (!err && sigint_event) {
result = WaitForSingleObject(sigint_event, 0);
if (result == WAIT_OBJECT_0) {
err = ERROR_CONTROL_C_EXIT;
} else if (result == WAIT_FAILED) {
err = GetLastError();
}
}
}
CloseHandle(cancel_event);
Py_END_ALLOW_THREADS
} else {
Py_BEGIN_ALLOW_THREADS
for (i = 0; i < thread_count; ++i) {
BatchedWaitData *data = thread_data[i];
data->thread = CreateThread(
NULL,
1, // smallest possible initial stack
_batched_WaitForMultipleObjects_thread,
(LPVOID)data,
CREATE_SUSPENDED,
NULL
);
if (!data->thread) {
err = GetLastError();
break;
}
handles[handle_count++] = data->thread;
}
Py_END_ALLOW_THREADS
if (err) {
PyErr_SetExcFromWindowsErr(PyExc_OSError, err);
goto error;
}
if (handle_count > MAXIMUM_WAIT_OBJECTS - 1) {
// basically an assert, but stronger
PyErr_SetString(PyExc_SystemError, "allocated too many wait objects");
goto error;
}
Py_BEGIN_ALLOW_THREADS
// Once we start resuming threads, can no longer "goto error"
for (i = 0; i < thread_count; ++i) {
ResumeThread(thread_data[i]->thread);
}
if (sigint_event) {
handles[handle_count++] = sigint_event;
}
result = WaitForMultipleObjects((DWORD)handle_count, handles, wait_all, milliseconds);
// ABANDONED is not possible here because we own all the handles
if (result == WAIT_FAILED) {
err = GetLastError();
} else if (result == WAIT_TIMEOUT) {
err = WAIT_TIMEOUT;
} else if (sigint_event && result == WAIT_OBJECT_0 + handle_count) {
err = ERROR_CONTROL_C_EXIT;
}
SetEvent(cancel_event);
// Wait for all threads to finish before we start freeing their memory
if (sigint_event) {
handle_count -= 1;
}
WaitForMultipleObjects((DWORD)handle_count, handles, TRUE, INFINITE);
for (i = 0; i < thread_count; ++i) {
if (!err && thread_data[i]->result == WAIT_FAILED) {
if (!GetExitCodeThread(thread_data[i]->thread, &err)) {
err = GetLastError();
}
}
CloseHandle(thread_data[i]->thread);
}
CloseHandle(cancel_event);
Py_END_ALLOW_THREADS
}
PyObject *triggered_indices;
if (sigint_event != NULL && err == ERROR_CONTROL_C_EXIT) {
errno = EINTR;
PyErr_SetFromErrno(PyExc_OSError);
triggered_indices = NULL;
} else if (err) {
PyErr_SetExcFromWindowsErr(PyExc_OSError, err);
triggered_indices = NULL;
} else if (wait_all) {
triggered_indices = Py_NewRef(Py_None);
} else {
triggered_indices = PyList_New(0);
if (triggered_indices) {
for (i = 0; i < thread_count; ++i) {
Py_ssize_t triggered = (Py_ssize_t)thread_data[i]->result - WAIT_OBJECT_0;
if (triggered >= 0 && triggered < thread_data[i]->handle_count - 1) {
PyObject *v = PyLong_FromSsize_t(thread_data[i]->handle_base + triggered);
if (!v || PyList_Append(triggered_indices, v) < 0) {
Py_XDECREF(v);
Py_CLEAR(triggered_indices);
break;
}
Py_DECREF(v);
}
}
}
}
for (i = 0; i < thread_count; ++i) {
PyMem_Free((void *)thread_data[i]);
}
return triggered_indices;
error:
// We should only enter here before any threads start running.
// Once we start resuming threads, different cleanup is required
CloseHandle(cancel_event);
while (--thread_count >= 0) {
HANDLE t = thread_data[thread_count]->thread;
if (t) {
TerminateThread(t, WAIT_ABANDONED_0);
CloseHandle(t);
}
PyMem_Free((void *)thread_data[thread_count]);
}
return NULL;
}
/*[clinic input]
_winapi.WaitForMultipleObjects
@ -2335,8 +2833,10 @@ _winapi_CopyFile2_impl(PyObject *module, LPCWSTR existing_file_name,
static PyMethodDef winapi_functions[] = {
_WINAPI_CLOSEHANDLE_METHODDEF
_WINAPI_CONNECTNAMEDPIPE_METHODDEF
_WINAPI_CREATEEVENTW_METHODDEF
_WINAPI_CREATEFILE_METHODDEF
_WINAPI_CREATEFILEMAPPING_METHODDEF
_WINAPI_CREATEMUTEXW_METHODDEF
_WINAPI_CREATENAMEDPIPE_METHODDEF
_WINAPI_CREATEPIPE_METHODDEF
_WINAPI_CREATEPROCESS_METHODDEF
@ -2350,17 +2850,23 @@ static PyMethodDef winapi_functions[] = {
_WINAPI_GETSTDHANDLE_METHODDEF
_WINAPI_GETVERSION_METHODDEF
_WINAPI_MAPVIEWOFFILE_METHODDEF
_WINAPI_OPENEVENTW_METHODDEF
_WINAPI_OPENFILEMAPPING_METHODDEF
_WINAPI_OPENMUTEXW_METHODDEF
_WINAPI_OPENPROCESS_METHODDEF
_WINAPI_PEEKNAMEDPIPE_METHODDEF
_WINAPI_LCMAPSTRINGEX_METHODDEF
_WINAPI_READFILE_METHODDEF
_WINAPI_RELEASEMUTEX_METHODDEF
_WINAPI_RESETEVENT_METHODDEF
_WINAPI_SETEVENT_METHODDEF
_WINAPI_SETNAMEDPIPEHANDLESTATE_METHODDEF
_WINAPI_TERMINATEPROCESS_METHODDEF
_WINAPI_UNMAPVIEWOFFILE_METHODDEF
_WINAPI_VIRTUALQUERYSIZE_METHODDEF
_WINAPI_WAITNAMEDPIPE_METHODDEF
_WINAPI_WAITFORMULTIPLEOBJECTS_METHODDEF
_WINAPI_BATCHEDWAITFORMULTIPLEOBJECTS_METHODDEF
_WINAPI_WAITFORSINGLEOBJECT_METHODDEF
_WINAPI_WRITEFILE_METHODDEF
_WINAPI_GETACP_METHODDEF

View File

@ -151,6 +151,76 @@ exit:
return return_value;
}
PyDoc_STRVAR(_winapi_CreateEventW__doc__,
"CreateEventW($module, /, security_attributes, manual_reset,\n"
" initial_state, name)\n"
"--\n"
"\n");
#define _WINAPI_CREATEEVENTW_METHODDEF \
{"CreateEventW", _PyCFunction_CAST(_winapi_CreateEventW), METH_FASTCALL|METH_KEYWORDS, _winapi_CreateEventW__doc__},
static HANDLE
_winapi_CreateEventW_impl(PyObject *module,
LPSECURITY_ATTRIBUTES security_attributes,
BOOL manual_reset, BOOL initial_state,
LPCWSTR name);
static PyObject *
_winapi_CreateEventW(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 4
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(security_attributes), &_Py_ID(manual_reset), &_Py_ID(initial_state), &_Py_ID(name), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"security_attributes", "manual_reset", "initial_state", "name", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "" F_POINTER "iiO&:CreateEventW",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
LPSECURITY_ATTRIBUTES security_attributes;
BOOL manual_reset;
BOOL initial_state;
LPCWSTR name = NULL;
HANDLE _return_value;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&security_attributes, &manual_reset, &initial_state, _PyUnicode_WideCharString_Opt_Converter, &name)) {
goto exit;
}
_return_value = _winapi_CreateEventW_impl(module, security_attributes, manual_reset, initial_state, name);
if ((_return_value == INVALID_HANDLE_VALUE) && PyErr_Occurred()) {
goto exit;
}
if (_return_value == NULL) {
Py_RETURN_NONE;
}
return_value = HANDLE_TO_PYNUM(_return_value);
exit:
/* Cleanup for name */
PyMem_Free((void *)name);
return return_value;
}
PyDoc_STRVAR(_winapi_CreateFile__doc__,
"CreateFile($module, file_name, desired_access, share_mode,\n"
" security_attributes, creation_disposition,\n"
@ -297,6 +367,73 @@ exit:
return return_value;
}
PyDoc_STRVAR(_winapi_CreateMutexW__doc__,
"CreateMutexW($module, /, security_attributes, initial_owner, name)\n"
"--\n"
"\n");
#define _WINAPI_CREATEMUTEXW_METHODDEF \
{"CreateMutexW", _PyCFunction_CAST(_winapi_CreateMutexW), METH_FASTCALL|METH_KEYWORDS, _winapi_CreateMutexW__doc__},
static HANDLE
_winapi_CreateMutexW_impl(PyObject *module,
LPSECURITY_ATTRIBUTES security_attributes,
BOOL initial_owner, LPCWSTR name);
static PyObject *
_winapi_CreateMutexW(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 3
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(security_attributes), &_Py_ID(initial_owner), &_Py_ID(name), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"security_attributes", "initial_owner", "name", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "" F_POINTER "iO&:CreateMutexW",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
LPSECURITY_ATTRIBUTES security_attributes;
BOOL initial_owner;
LPCWSTR name = NULL;
HANDLE _return_value;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&security_attributes, &initial_owner, _PyUnicode_WideCharString_Opt_Converter, &name)) {
goto exit;
}
_return_value = _winapi_CreateMutexW_impl(module, security_attributes, initial_owner, name);
if ((_return_value == INVALID_HANDLE_VALUE) && PyErr_Occurred()) {
goto exit;
}
if (_return_value == NULL) {
Py_RETURN_NONE;
}
return_value = HANDLE_TO_PYNUM(_return_value);
exit:
/* Cleanup for name */
PyMem_Free((void *)name);
return return_value;
}
PyDoc_STRVAR(_winapi_CreateNamedPipe__doc__,
"CreateNamedPipe($module, name, open_mode, pipe_mode, max_instances,\n"
" out_buffer_size, in_buffer_size, default_timeout,\n"
@ -771,6 +908,138 @@ exit:
return return_value;
}
PyDoc_STRVAR(_winapi_OpenEventW__doc__,
"OpenEventW($module, /, desired_access, inherit_handle, name)\n"
"--\n"
"\n");
#define _WINAPI_OPENEVENTW_METHODDEF \
{"OpenEventW", _PyCFunction_CAST(_winapi_OpenEventW), METH_FASTCALL|METH_KEYWORDS, _winapi_OpenEventW__doc__},
static HANDLE
_winapi_OpenEventW_impl(PyObject *module, DWORD desired_access,
BOOL inherit_handle, LPCWSTR name);
static PyObject *
_winapi_OpenEventW(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 3
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(desired_access), &_Py_ID(inherit_handle), &_Py_ID(name), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"desired_access", "inherit_handle", "name", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "kiO&:OpenEventW",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
DWORD desired_access;
BOOL inherit_handle;
LPCWSTR name = NULL;
HANDLE _return_value;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&desired_access, &inherit_handle, _PyUnicode_WideCharString_Converter, &name)) {
goto exit;
}
_return_value = _winapi_OpenEventW_impl(module, desired_access, inherit_handle, name);
if ((_return_value == INVALID_HANDLE_VALUE) && PyErr_Occurred()) {
goto exit;
}
if (_return_value == NULL) {
Py_RETURN_NONE;
}
return_value = HANDLE_TO_PYNUM(_return_value);
exit:
/* Cleanup for name */
PyMem_Free((void *)name);
return return_value;
}
PyDoc_STRVAR(_winapi_OpenMutexW__doc__,
"OpenMutexW($module, /, desired_access, inherit_handle, name)\n"
"--\n"
"\n");
#define _WINAPI_OPENMUTEXW_METHODDEF \
{"OpenMutexW", _PyCFunction_CAST(_winapi_OpenMutexW), METH_FASTCALL|METH_KEYWORDS, _winapi_OpenMutexW__doc__},
static HANDLE
_winapi_OpenMutexW_impl(PyObject *module, DWORD desired_access,
BOOL inherit_handle, LPCWSTR name);
static PyObject *
_winapi_OpenMutexW(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 3
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(desired_access), &_Py_ID(inherit_handle), &_Py_ID(name), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"desired_access", "inherit_handle", "name", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "kiO&:OpenMutexW",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
DWORD desired_access;
BOOL inherit_handle;
LPCWSTR name = NULL;
HANDLE _return_value;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&desired_access, &inherit_handle, _PyUnicode_WideCharString_Converter, &name)) {
goto exit;
}
_return_value = _winapi_OpenMutexW_impl(module, desired_access, inherit_handle, name);
if ((_return_value == INVALID_HANDLE_VALUE) && PyErr_Occurred()) {
goto exit;
}
if (_return_value == NULL) {
Py_RETURN_NONE;
}
return_value = HANDLE_TO_PYNUM(_return_value);
exit:
/* Cleanup for name */
PyMem_Free((void *)name);
return return_value;
}
PyDoc_STRVAR(_winapi_OpenFileMapping__doc__,
"OpenFileMapping($module, desired_access, inherit_handle, name, /)\n"
"--\n"
@ -991,6 +1260,162 @@ exit:
return return_value;
}
PyDoc_STRVAR(_winapi_ReleaseMutex__doc__,
"ReleaseMutex($module, /, mutex)\n"
"--\n"
"\n");
#define _WINAPI_RELEASEMUTEX_METHODDEF \
{"ReleaseMutex", _PyCFunction_CAST(_winapi_ReleaseMutex), METH_FASTCALL|METH_KEYWORDS, _winapi_ReleaseMutex__doc__},
static PyObject *
_winapi_ReleaseMutex_impl(PyObject *module, HANDLE mutex);
static PyObject *
_winapi_ReleaseMutex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(mutex), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"mutex", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "" F_HANDLE ":ReleaseMutex",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
HANDLE mutex;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&mutex)) {
goto exit;
}
return_value = _winapi_ReleaseMutex_impl(module, mutex);
exit:
return return_value;
}
PyDoc_STRVAR(_winapi_ResetEvent__doc__,
"ResetEvent($module, /, event)\n"
"--\n"
"\n");
#define _WINAPI_RESETEVENT_METHODDEF \
{"ResetEvent", _PyCFunction_CAST(_winapi_ResetEvent), METH_FASTCALL|METH_KEYWORDS, _winapi_ResetEvent__doc__},
static PyObject *
_winapi_ResetEvent_impl(PyObject *module, HANDLE event);
static PyObject *
_winapi_ResetEvent(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(event), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"event", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "" F_HANDLE ":ResetEvent",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
HANDLE event;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&event)) {
goto exit;
}
return_value = _winapi_ResetEvent_impl(module, event);
exit:
return return_value;
}
PyDoc_STRVAR(_winapi_SetEvent__doc__,
"SetEvent($module, /, event)\n"
"--\n"
"\n");
#define _WINAPI_SETEVENT_METHODDEF \
{"SetEvent", _PyCFunction_CAST(_winapi_SetEvent), METH_FASTCALL|METH_KEYWORDS, _winapi_SetEvent__doc__},
static PyObject *
_winapi_SetEvent_impl(PyObject *module, HANDLE event);
static PyObject *
_winapi_SetEvent(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 1
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(event), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"event", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "" F_HANDLE ":SetEvent",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
HANDLE event;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&event)) {
goto exit;
}
return_value = _winapi_SetEvent_impl(module, event);
exit:
return return_value;
}
PyDoc_STRVAR(_winapi_SetNamedPipeHandleState__doc__,
"SetNamedPipeHandleState($module, named_pipe, mode,\n"
" max_collection_count, collect_data_timeout, /)\n"
@ -1114,6 +1539,77 @@ exit:
return return_value;
}
PyDoc_STRVAR(_winapi_BatchedWaitForMultipleObjects__doc__,
"BatchedWaitForMultipleObjects($module, /, handle_seq, wait_all,\n"
" milliseconds=_winapi.INFINITE)\n"
"--\n"
"\n"
"Supports a larger number of handles than WaitForMultipleObjects\n"
"\n"
"Note that the handles may be waited on other threads, which could cause\n"
"issues for objects like mutexes that become associated with the thread\n"
"that was waiting for them. Objects may also be left signalled, even if\n"
"the wait fails.\n"
"\n"
"It is recommended to use WaitForMultipleObjects whenever possible, and\n"
"only switch to BatchedWaitForMultipleObjects for scenarios where you\n"
"control all the handles involved, such as your own thread pool or\n"
"files, and all wait objects are left unmodified by a wait (for example,\n"
"manual reset events, threads, and files/pipes).\n"
"\n"
"Overlapped handles returned from this module use manual reset events.");
#define _WINAPI_BATCHEDWAITFORMULTIPLEOBJECTS_METHODDEF \
{"BatchedWaitForMultipleObjects", _PyCFunction_CAST(_winapi_BatchedWaitForMultipleObjects), METH_FASTCALL|METH_KEYWORDS, _winapi_BatchedWaitForMultipleObjects__doc__},
static PyObject *
_winapi_BatchedWaitForMultipleObjects_impl(PyObject *module,
PyObject *handle_seq,
BOOL wait_all, DWORD milliseconds);
static PyObject *
_winapi_BatchedWaitForMultipleObjects(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 3
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(handle_seq), &_Py_ID(wait_all), &_Py_ID(milliseconds), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"handle_seq", "wait_all", "milliseconds", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.format = "Oi|k:BatchedWaitForMultipleObjects",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *handle_seq;
BOOL wait_all;
DWORD milliseconds = INFINITE;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&handle_seq, &wait_all, &milliseconds)) {
goto exit;
}
return_value = _winapi_BatchedWaitForMultipleObjects_impl(module, handle_seq, wait_all, milliseconds);
exit:
return return_value;
}
PyDoc_STRVAR(_winapi_WaitForMultipleObjects__doc__,
"WaitForMultipleObjects($module, handle_seq, wait_flag,\n"
" milliseconds=_winapi.INFINITE, /)\n"
@ -1482,4 +1978,4 @@ exit:
return return_value;
}
/*[clinic end generated code: output=2350d4f2275d3a6f input=a9049054013a1b77]*/
/*[clinic end generated code: output=1f5bbcfa8d1847c5 input=a9049054013a1b77]*/

View File

@ -3539,7 +3539,6 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning,
#undef EOPNOTSUPP
#undef EPROTONOSUPPORT
#undef EPROTOTYPE
#undef ETIMEDOUT
#undef EWOULDBLOCK
#if defined(WSAEALREADY) && !defined(EALREADY)
@ -3560,9 +3559,6 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning,
#if defined(WSAESHUTDOWN) && !defined(ESHUTDOWN)
#define ESHUTDOWN WSAESHUTDOWN
#endif
#if defined(WSAETIMEDOUT) && !defined(ETIMEDOUT)
#define ETIMEDOUT WSAETIMEDOUT
#endif
#if defined(WSAEWOULDBLOCK) && !defined(EWOULDBLOCK)
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
@ -3747,6 +3743,9 @@ _PyExc_InitState(PyInterpreterState *interp)
#endif
ADD_ERRNO(ProcessLookupError, ESRCH);
ADD_ERRNO(TimeoutError, ETIMEDOUT);
#ifdef WSAETIMEDOUT
ADD_ERRNO(TimeoutError, WSAETIMEDOUT);
#endif
return _PyStatus_OK();

View File

@ -129,6 +129,9 @@ winerror_to_errno(int winerror)
case ERROR_NO_UNICODE_TRANSLATION: // 1113
return EILSEQ;
case WAIT_TIMEOUT: // 258
return ETIMEDOUT;
case ERROR_INVALID_FUNCTION: // 1
case ERROR_INVALID_ACCESS: // 12
case ERROR_INVALID_DATA: // 13