| 
4 | 4 | import os  | 
5 | 5 | import random  | 
6 | 6 | import sys  | 
 | 7 | +import subprocess  | 
7 | 8 | import threading  | 
8 | 9 | import time  | 
9 | 10 | import uvloop  | 
@@ -735,6 +736,69 @@ def scheduler():  | 
735 | 736 |         thread.join()  | 
736 | 737 |         self.assertEqual(counter[0], ITERATIONS)  | 
737 | 738 | 
 
  | 
 | 739 | +    def test_freethreading(self):  | 
 | 740 | +        if not hasattr(sys, "_is_gil_enabled"):  | 
 | 741 | +            raise unittest.SkipTest("No sys._is_gil_enabled()")  | 
 | 742 | +        if os.cpu_count() < 2:  | 
 | 743 | +            raise unittest.SkipTest("Flaky on single CPU machines")  | 
 | 744 | +        prog = """\  | 
 | 745 | +import asyncio  | 
 | 746 | +import os  | 
 | 747 | +import sys  | 
 | 748 | +import threading  | 
 | 749 | +import time  | 
 | 750 | +
  | 
 | 751 | +
  | 
 | 752 | +counter = 0  | 
 | 753 | +
  | 
 | 754 | +
  | 
 | 755 | +def job(barrier):  | 
 | 756 | +    global counter  | 
 | 757 | +    barrier.wait()  | 
 | 758 | +    start_time = time.monotonic()  | 
 | 759 | +    rv = 0  | 
 | 760 | +    while time.monotonic() - start_time < 1:  | 
 | 761 | +        for _i in range(10**4):  | 
 | 762 | +            counter += 1  | 
 | 763 | +            rv += 1  | 
 | 764 | +    return rv  | 
 | 765 | +
  | 
 | 766 | +
  | 
 | 767 | +async def main():  | 
 | 768 | +    if sys._is_gil_enabled():  | 
 | 769 | +        print("{impl} turned on GIL")  | 
 | 770 | +        return False  | 
 | 771 | +    loop = asyncio.get_running_loop()  | 
 | 772 | +    n_jobs = os.cpu_count()  | 
 | 773 | +    barrier = threading.Barrier(n_jobs)  | 
 | 774 | +    fs = [loop.run_in_executor(None, job, barrier) for _ in range(n_jobs)]  | 
 | 775 | +    result = sum(await asyncio.gather(*fs))  | 
 | 776 | +    if counter == result:  | 
 | 777 | +        print("Expected race condition did not happen")  | 
 | 778 | +        return False  | 
 | 779 | +    return True  | 
 | 780 | +
  | 
 | 781 | +
  | 
 | 782 | +if __name__ == "__main__":  | 
 | 783 | +    if sys._is_gil_enabled():  | 
 | 784 | +        print("Not running with GIL disabled")  | 
 | 785 | +        sys.exit(2)  | 
 | 786 | +
  | 
 | 787 | +    import {impl}  | 
 | 788 | +
  | 
 | 789 | +    if not {impl}.run(main()):  | 
 | 790 | +        sys.exit(1)  | 
 | 791 | +"""  | 
 | 792 | +        result = subprocess.run(  | 
 | 793 | +            [sys.executable, '-c', prog.format(impl=self.implementation)],  | 
 | 794 | +            stdout=subprocess.PIPE,  | 
 | 795 | +            text=True,  | 
 | 796 | +        )  | 
 | 797 | +        if result.returncode == 2:  | 
 | 798 | +            raise unittest.SkipTest(result.stdout.strip())  | 
 | 799 | +        elif result.returncode != 0:  | 
 | 800 | +            self.fail(result.stdout.strip())  | 
 | 801 | + | 
738 | 802 | 
 
  | 
739 | 803 | class TestBaseUV(_TestBase, UVTestCase):  | 
740 | 804 | 
 
  | 
 | 
0 commit comments