|
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