gh-110693: Pending Calls Machinery Cleanups (gh-118296)

This does some cleanup in preparation for later changes.
This commit is contained in:
Eric Snow 2024-04-25 19:05:51 -06:00 committed by GitHub
parent d5df25268b
commit 09c2947581
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 319 additions and 117 deletions

View file

@ -1172,6 +1172,12 @@ class CAPITest(unittest.TestCase):
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
def test_gen_get_code(self):
def genf(): yield
gen = genf()
self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code)
@requires_limited_api
class TestHeapTypeRelative(unittest.TestCase):
"""Test API for extending opaque types (PEP 697)"""
@ -1452,7 +1458,7 @@ class TestPendingCalls(unittest.TestCase):
# about when pending calls get run. This is especially relevant
# here for creating deterministic tests.
def pendingcalls_submit(self, l, n):
def main_pendingcalls_submit(self, l, n):
def callback():
#this function can be interrupted by thread switching so let's
#use an atomic operation
@ -1467,12 +1473,27 @@ class TestPendingCalls(unittest.TestCase):
if _testcapi._pending_threadfunc(callback):
break
def pendingcalls_wait(self, l, n, context = None):
def pendingcalls_submit(self, l, n, *, main=True, ensure=False):
def callback():
#this function can be interrupted by thread switching so let's
#use an atomic operation
l.append(None)
if main:
return _testcapi._pending_threadfunc(callback, n,
blocking=False,
ensure_added=ensure)
else:
return _testinternalcapi.pending_threadfunc(callback, n,
blocking=False,
ensure_added=ensure)
def pendingcalls_wait(self, l, numadded, context = None):
#now, stick around until l[0] has grown to 10
count = 0
while len(l) != n:
while len(l) != numadded:
#this busy loop is where we expect to be interrupted to
#run our callbacks. Note that callbacks are only run on the
#run our callbacks. Note that some callbacks are only run on the
#main thread
if False and support.verbose:
print("(%i)"%(len(l),),)
@ -1482,12 +1503,12 @@ class TestPendingCalls(unittest.TestCase):
continue
count += 1
self.assertTrue(count < 10000,
"timeout waiting for %i callbacks, got %i"%(n, len(l)))
"timeout waiting for %i callbacks, got %i"%(numadded, len(l)))
if False and support.verbose:
print("(%i)"%(len(l),))
@threading_helper.requires_working_threading()
def test_pendingcalls_threaded(self):
def test_main_pendingcalls_threaded(self):
#do every callback on a separate thread
n = 32 #total callbacks
@ -1501,15 +1522,15 @@ class TestPendingCalls(unittest.TestCase):
context.lock = threading.Lock()
context.event = threading.Event()
threads = [threading.Thread(target=self.pendingcalls_thread,
threads = [threading.Thread(target=self.main_pendingcalls_thread,
args=(context,))
for i in range(context.nThreads)]
with threading_helper.start_threads(threads):
self.pendingcalls_wait(context.l, n, context)
def pendingcalls_thread(self, context):
def main_pendingcalls_thread(self, context):
try:
self.pendingcalls_submit(context.l, context.n)
self.main_pendingcalls_submit(context.l, context.n)
finally:
with context.lock:
context.nFinished += 1
@ -1519,20 +1540,54 @@ class TestPendingCalls(unittest.TestCase):
if nFinished == context.nThreads:
context.event.set()
def test_pendingcalls_non_threaded(self):
def test_main_pendingcalls_non_threaded(self):
#again, just using the main thread, likely they will all be dispatched at
#once. It is ok to ask for too many, because we loop until we find a slot.
#the loop can be interrupted to dispatch.
#there are only 32 dispatch slots, so we go for twice that!
l = []
n = 64
self.pendingcalls_submit(l, n)
self.main_pendingcalls_submit(l, n)
self.pendingcalls_wait(l, n)
def test_gen_get_code(self):
def genf(): yield
gen = genf()
self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code)
def test_max_pending(self):
with self.subTest('main-only'):
maxpending = 32
l = []
added = self.pendingcalls_submit(l, 1, main=True)
self.pendingcalls_wait(l, added)
self.assertEqual(added, 1)
l = []
added = self.pendingcalls_submit(l, maxpending, main=True)
self.pendingcalls_wait(l, added)
self.assertEqual(added, maxpending)
l = []
added = self.pendingcalls_submit(l, maxpending+1, main=True)
self.pendingcalls_wait(l, added)
self.assertEqual(added, maxpending)
with self.subTest('not main-only'):
# Per-interpreter pending calls has the same low limit
# on how many may be pending at a time.
maxpending = 32
l = []
added = self.pendingcalls_submit(l, 1, main=False)
self.pendingcalls_wait(l, added)
self.assertEqual(added, 1)
l = []
added = self.pendingcalls_submit(l, maxpending, main=False)
self.pendingcalls_wait(l, added)
self.assertEqual(added, maxpending)
l = []
added = self.pendingcalls_submit(l, maxpending+1, main=False)
self.pendingcalls_wait(l, added)
self.assertEqual(added, maxpending)
class PendingTask(types.SimpleNamespace):