|
4 | 4 | import gc |
5 | 5 | import math |
6 | 6 | import operator |
| 7 | +import threading |
7 | 8 | import unittest |
8 | 9 | import platform |
9 | 10 | import struct |
@@ -39,6 +40,24 @@ def bigendian_to_native(value): |
39 | 40 | else: |
40 | 41 | return string_reverse(value) |
41 | 42 |
|
| 43 | +# Helper for test_endian_table_init_subinterpreters |
| 44 | +class _Result: |
| 45 | + def __init__(self): |
| 46 | + self.ret = -1 |
| 47 | + self.err = None |
| 48 | + |
| 49 | +def _run_in_subinterp_worker(code, barrier, result): |
| 50 | + """ |
| 51 | + Worker function for a thread. Waits on the barrier, then runs the |
| 52 | + code in a subinterpreter. |
| 53 | + """ |
| 54 | + try: |
| 55 | + # Wait until all threads are ready to start simultaneously. |
| 56 | + barrier.wait() |
| 57 | + result.ret = support.run_in_subinterp(code) |
| 58 | + except Exception as e: |
| 59 | + result.err = e |
| 60 | + |
42 | 61 | class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase): |
43 | 62 | def test_isbigendian(self): |
44 | 63 | self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN) |
@@ -800,6 +819,44 @@ def test_c_complex_round_trip(self): |
800 | 819 | round_trip = struct.unpack(f, struct.pack(f, z))[0] |
801 | 820 | self.assertComplexesAreIdentical(z, round_trip) |
802 | 821 |
|
| 822 | + def test_endian_table_init_subinterpreters(self): |
| 823 | + # Verify that struct works correctly in subinterpreters after |
| 824 | + # once-only endian table initialization (gh-140260), when |
| 825 | + # initialized concurrently. |
| 826 | + # Use struct in the main interpreter first. |
| 827 | + self.assertEqual(struct.unpack('>i', struct.pack('>i', 1))[0], 1) |
| 828 | + self.assertEqual(struct.unpack('<i', struct.pack('<i', 1))[0], 1) |
| 829 | + |
| 830 | + code = ( |
| 831 | + "import struct\n" |
| 832 | + "x = struct.pack('>i', 1)\n" |
| 833 | + "assert struct.unpack('>i', x)[0] == 1\n" |
| 834 | + "y = struct.pack('<i', 1)\n" |
| 835 | + "assert struct.unpack('<i', y)[0] == 1\n" |
| 836 | + ) |
| 837 | + |
| 838 | + num_threads = 3 |
| 839 | + barrier = threading.Barrier(num_threads) |
| 840 | + results = [_Result() for _ in range(num_threads)] |
| 841 | + threads = [ |
| 842 | + threading.Thread( |
| 843 | + target=_run_in_subinterp_worker, |
| 844 | + args=(code, barrier, results[i]) |
| 845 | + ) |
| 846 | + for i in range(num_threads) |
| 847 | + ] |
| 848 | + |
| 849 | + for t in threads: |
| 850 | + t.start() |
| 851 | + |
| 852 | + for t in threads: |
| 853 | + t.join() |
| 854 | + |
| 855 | + for result in results: |
| 856 | + if result.err: |
| 857 | + raise result.err |
| 858 | + self.assertEqual(result.ret, 0) |
| 859 | + |
803 | 860 |
|
804 | 861 | class UnpackIteratorTest(unittest.TestCase): |
805 | 862 | """ |
|
0 commit comments