round_trip = struct.unpack(f, struct.pack(f, z))[0]
self.assertComplexesAreIdentical(z, round_trip)
+ @unittest.skipIf(
+ support.is_android or support.is_apple_mobile,
+ "Subinterpreters are not supported on Android and iOS"
+ )
+ def test_endian_table_init_subinterpreters(self):
+ # Verify that the _struct extension module can be initialized
+ # concurrently in subinterpreters (gh-140260).
+ try:
+ from concurrent.futures import InterpreterPoolExecutor
+ except ImportError:
+ raise unittest.SkipTest("InterpreterPoolExecutor not available")
+
+ code = "import struct"
+ with InterpreterPoolExecutor(max_workers=5) as executor:
+ results = executor.map(exec, [code] * 5)
+ self.assertListEqual(list(results), [None] * 5)
+
class UnpackIteratorTest(unittest.TestCase):
"""
#include "Python.h"
#include "pycore_bytesobject.h" // _PyBytesWriter
+#include "pycore_lock.h" // _PyOnceFlag_CallOnce()
#include "pycore_long.h" // _PyLong_AsByteArray()
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
{0}
};
+/* Ensure endian table optimization happens exactly once across all interpreters */
+static _PyOnceFlag endian_tables_init_once = {0};
+
+static int
+init_endian_tables(void *Py_UNUSED(arg))
+{
+ const formatdef *native = native_table;
+ formatdef *other, *ptr;
+#if PY_LITTLE_ENDIAN
+ other = lilendian_table;
+#else
+ other = bigendian_table;
+#endif
+ /* Scan through the native table, find a matching
+ entry in the endian table and swap in the
+ native implementations whenever possible
+ (64-bit platforms may not have "standard" sizes) */
+ while (native->format != '\0' && other->format != '\0') {
+ ptr = other;
+ while (ptr->format != '\0') {
+ if (ptr->format == native->format) {
+ /* Match faster when formats are
+ listed in the same order */
+ if (ptr == other)
+ other++;
+ /* Only use the trick if the
+ size matches */
+ if (ptr->size != native->size)
+ break;
+ /* Skip float and double, could be
+ "unknown" float format */
+ if (ptr->format == 'd' || ptr->format == 'f')
+ break;
+ /* Skip _Bool, semantics are different for standard size */
+ if (ptr->format == '?')
+ break;
+ ptr->pack = native->pack;
+ ptr->unpack = native->unpack;
+ break;
+ }
+ ptr++;
+ }
+ native++;
+ }
+ return 0;
+}
+
static const formatdef *
whichtable(const char **pfmt)
return -1;
}
- /* Check endian and swap in faster functions */
- {
- const formatdef *native = native_table;
- formatdef *other, *ptr;
-#if PY_LITTLE_ENDIAN
- other = lilendian_table;
-#else
- other = bigendian_table;
-#endif
- /* Scan through the native table, find a matching
- entry in the endian table and swap in the
- native implementations whenever possible
- (64-bit platforms may not have "standard" sizes) */
- while (native->format != '\0' && other->format != '\0') {
- ptr = other;
- while (ptr->format != '\0') {
- if (ptr->format == native->format) {
- /* Match faster when formats are
- listed in the same order */
- if (ptr == other)
- other++;
- /* Only use the trick if the
- size matches */
- if (ptr->size != native->size)
- break;
- /* Skip float and double, could be
- "unknown" float format */
- if (ptr->format == 'd' || ptr->format == 'f')
- break;
- /* Skip _Bool, semantics are different for standard size */
- if (ptr->format == '?')
- break;
- ptr->pack = native->pack;
- ptr->unpack = native->unpack;
- break;
- }
- ptr++;
- }
- native++;
- }
- }
+ /* init cannot fail */
+ (void)_PyOnceFlag_CallOnce(&endian_tables_init_once, init_endian_tables, NULL);
/* Add some symbolic constants to the module */
state->StructError = PyErr_NewException("struct.error", NULL, NULL);