def test__count(self): # Test the _count() function. orig = thread._count() mut = thread.allocate_lock() mut.acquire() started = [] def task(): started.append(None) mut.acquire() mut.release() with threading_helper.wait_threads_exit(): thread.start_new_thread(task, ()) for _ in support.sleeping_retry(support.LONG_TIMEOUT): if started: break self.assertEqual(thread._count(), orig + 1) # Allow the task to finish. mut.release() # The only reliable way to be sure that the thread ended from the # interpreter's point of view is to wait for the function object to # be destroyed. done = [] wr = weakref.ref(task, lambda _: done.append(None)) del task for _ in support.sleeping_retry(support.LONG_TIMEOUT): if done: break support.gc_collect() # For PyPy or other GCs. self.assertEqual(thread._count(), orig)
def wait_threads_exit(timeout=None): """ bpo-31234: Context manager to wait until all threads created in the with statement exit. Use _thread.count() to check if threads exited. Indirectly, wait until threads exit the internal t_bootstrap() C function of the _thread module. threading_setup() and threading_cleanup() are designed to emit a warning if a test leaves running threads in the background. This context manager is designed to cleanup threads started by the _thread.start_new_thread() which doesn't allow to wait for thread exit, whereas thread.Thread has a join() method. """ if timeout is None: timeout = support.SHORT_TIMEOUT old_count = _thread._count() try: yield finally: start_time = time.monotonic() for _ in support.sleeping_retry(timeout, error=False): support.gc_collect() count = _thread._count() if count <= old_count: break else: dt = time.monotonic() - start_time msg = (f"wait_threads() failed to cleanup {count - old_count} " f"threads after {dt:.1f} seconds " f"(count: {count}, old count: {old_count})") raise AssertionError(msg)
def test_wait(self): for i in range(NUM_THREADS): thread = threading.Thread(target=self.f, args=(i, )) thread.start() self.threads.append(thread) # busy-loop to wait for threads for _ in support.sleeping_retry(support.SHORT_TIMEOUT): if len(self.alive) >= NUM_THREADS: break a = sorted(self.alive.keys()) self.assertEqual(a, list(range(NUM_THREADS))) prefork_lives = self.alive.copy() if sys.platform in ['unixware7']: cpid = os.fork1() else: cpid = os.fork() if cpid == 0: # Child time.sleep(LONGSLEEP) n = 0 for key in self.alive: if self.alive[key] != prefork_lives[key]: n += 1 os._exit(n) else: # Parent self.wait_impl(cpid, exitcode=0)
def _lock(self, lock_func, lock_name): self.addCleanup(os_helper.unlink, os_helper.TESTFN) code = '\n'.join(("import fcntl, time", "with open('%s', 'wb') as f:" % os_helper.TESTFN, " fcntl.%s(f, fcntl.LOCK_EX)" % lock_name, " time.sleep(%s)" % self.sleep_time)) start_time = time.monotonic() proc = self.subprocess(code) with kill_on_error(proc): with open(os_helper.TESTFN, 'wb') as f: # synchronize the subprocess start_time = time.monotonic() for _ in support.sleeping_retry(support.LONG_TIMEOUT, error=False): try: lock_func(f, fcntl.LOCK_EX | fcntl.LOCK_NB) lock_func(f, fcntl.LOCK_UN) except BlockingIOError: break else: dt = time.monotonic() - start_time raise Exception("failed to sync child in %.1f sec" % dt) # the child locked the file just a moment ago for 'sleep_time' seconds # that means that the lock below will block for 'sleep_time' minus some # potential context switch delay lock_func(f, fcntl.LOCK_EX) dt = time.monotonic() - start_time self.assertGreaterEqual(dt, self.sleep_time) self.stop_alarm() proc.wait()
def wait_impl(self, cpid, *, exitcode): # This many iterations can be required, since some previously run # tests (e.g. test_ctypes) could have spawned a lot of children # very quickly. for _ in support.sleeping_retry(support.SHORT_TIMEOUT): # wait3() shouldn't hang, but some of the buildbots seem to hang # in the forking tests. This is an attempt to fix the problem. spid, status, rusage = os.wait3(os.WNOHANG) if spid == cpid: break self.assertEqual(spid, cpid) self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertTrue(rusage)
def wait_impl(self, cpid, *, exitcode): option = os.WNOHANG if sys.platform.startswith('aix'): # Issue #11185: wait4 is broken on AIX and will always return 0 # with WNOHANG. option = 0 for _ in support.sleeping_retry(support.SHORT_TIMEOUT): # wait4() shouldn't hang, but some of the buildbots seem to hang # in the forking tests. This is an attempt to fix the problem. spid, status, rusage = os.wait4(cpid, option) if spid == cpid: break self.assertEqual(spid, cpid) self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertTrue(rusage)