Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-librt for openSUSE:Factory 
checked in at 2026-10-06 19:13:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-librt (Old)
 and      /work/SRC/openSUSE:Factory/.python-librt.new.3698736 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-librt"

Tue Oct  6 19:13:22 2026 rev:8 rq:1382324 version:0.16.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-librt/python-librt.changes        
2026-08-24 12:02:07.321712846 +0200
+++ /work/SRC/openSUSE:Factory/.python-librt.new.3698736/python-librt.changes   
2026-10-06 19:13:25.438966680 +0200
@@ -1,0 +2,6 @@
+Sun Oct  4 14:35:14 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.16.0:
+  * Sync mypy and bump version
+
+-------------------------------------------------------------------

Old:
----
  librt-0.15.0.tar.gz

New:
----
  librt-0.16.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-librt.spec ++++++
--- /var/tmp/diff_new_pack.r8lYC3/_old  2026-10-06 19:13:26.087993833 +0200
+++ /var/tmp/diff_new_pack.r8lYC3/_new  2026-10-06 19:13:26.089993917 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-librt
-Version:        0.15.0
+Version:        0.16.0
 Release:        0
 Summary:        Mypyc runtime library
 License:        MIT

++++++ librt-0.15.0.tar.gz -> librt-0.16.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/CPy.h new/librt-0.16.0/CPy.h
--- old/librt-0.15.0/CPy.h      2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/CPy.h      2026-09-29 01:22:41.000000000 +0200
@@ -22,6 +22,52 @@
 #define CPYTHON_LARGE_INT_ERRMSG "Python int too large to convert to C ssize_t"
 
 
+// Native module import synchronization
+
+typedef struct CPyModuleLockAPI CPyModuleLockAPI;
+
+typedef struct {
+    int32_t initialized;
+    int32_t executed;
+} CPyImportState;
+
+enum {
+    CPY_LOCK_ERROR = -1,
+    CPY_LOCK_ACQUIRED = 0,
+    CPY_LOCK_DEADLOCK = 1,
+};
+
+CPyModuleLockAPI *CPyModuleLockAPI_Alloc(void);
+void CPyModuleLockAPI_Free(CPyModuleLockAPI *api);
+int CPyImport_AcquireLock(CPyModuleLockAPI *api, PyObject *module_name,
+                          PyObject **module_lock);
+int CPyImport_ReleaseLock(PyObject *module_lock);
+bool CPyImport_IsModuleInitializing(PyObject *module);
+bool CPyImport_IsInitialized(const CPyImportState *state);
+void CPyImport_SetInitialized(CPyImportState *state, bool initialized);
+bool CPyImport_IsExecuted(const CPyImportState *state);
+void CPyImport_SetExecuted(CPyImportState *state);
+
+#define CPY_MODULE_CACHE_UNVERIFIED ((CPyModuleCache)1)
+
+static inline CPyModuleCache CPyImport_LoadModuleCache(CPyModuleCache *cache) {
+#if PY_VERSION_HEX >= 0x030D0000
+    return _Py_atomic_load_uintptr_acquire(cache);
+#else
+    return *cache;
+#endif
+}
+
+static inline PyObject *CPyImport_GetModuleCache(CPyModuleCache *cache) {
+    return (PyObject *)(CPyImport_LoadModuleCache(cache) & 
~CPY_MODULE_CACHE_UNVERIFIED);
+}
+
+PyObject *CPyImport_GetModuleCacheForImport(CPyModuleCache *cache, PyObject 
*module_name);
+void CPyImport_ReplaceModuleCache(CPyModuleCache *cache, PyObject *module);
+void CPyImport_ReplaceModuleCacheUnverified(CPyModuleCache *cache, PyObject 
*module);
+void CPyImport_ReplaceModuleCacheForImport(CPyModuleCache *cache, PyObject 
*module);
+
+
 // Naming conventions:
 //
 // Tagged: tagged int
@@ -107,13 +153,13 @@
 #define CPY_GET_ATTR_TRAIT(obj, trait, vtable_index, object_type, attr_type)   
\
     ((attr_type (*)(object_type *))(CPy_FindTraitVtable(trait, ((object_type 
*)obj)->vtable))[vtable_index])((object_type *)obj)
 
-// Set attribute value using vtable
-#define CPY_SET_ATTR(obj, type, vtable_index, value, object_type, attr_type) \
-    ((bool (*)(object_type *, attr_type))((object_type 
*)obj)->vtable[vtable_index])( \
+// Set attribute value using vtable.
+#define CPY_SET_ATTR(obj, type, vtable_index, value, object_type, attr_type, 
ret_type) \
+    ((ret_type (*)(object_type *, attr_type))((object_type 
*)obj)->vtable[vtable_index])( \
         (object_type *)obj, value)
 
-#define CPY_SET_ATTR_TRAIT(obj, trait, vtable_index, value, object_type, 
attr_type) \
-    ((bool (*)(object_type *, attr_type))(CPy_FindTraitVtable(trait, 
((object_type *)obj)->vtable))[vtable_index])( \
+#define CPY_SET_ATTR_TRAIT(obj, trait, vtable_index, value, object_type, 
attr_type, ret_type) \
+    ((ret_type (*)(object_type *, attr_type))(CPy_FindTraitVtable(trait, 
((object_type *)obj)->vtable))[vtable_index])( \
         (object_type *)obj, value)
 
 #define CPY_GET_METHOD(obj, type, vtable_index, object_type, method_type) \
@@ -658,6 +704,7 @@
 PyObject *CPyIter_Next(PyObject *iter);
 PyObject *CPyNumber_Power(PyObject *base, PyObject *index);
 PyObject *CPyNumber_InPlacePower(PyObject *base, PyObject *index);
+// An omitted slice bound is represented by CPY_INT_TAG.
 PyObject *CPyObject_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end);
 
 
@@ -862,11 +909,37 @@
 
 // Tuple operations
 
-
-PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index);
 PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged 
end);
-PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index);
-void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, 
PyObject *value);
+PyObject *CPySequenceTuple_GetItem_(PyObject *tuple, CPyTagged index);
+
+static inline PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged 
index)
+{
+    if (likely(CPyTagged_CheckShort(index) && !CPyTagged_IsNegative(index))) {
+        Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
+        Py_ssize_t size = PyTuple_GET_SIZE(tuple);
+        if (unlikely(n >= size)) {
+            PyErr_SetString(PyExc_IndexError, "tuple index out of range");
+            return NULL;
+        }
+        PyObject *result = PyTuple_GET_ITEM(tuple, n);
+        Py_INCREF(result);
+        return result;
+    } else {
+        return CPySequenceTuple_GetItem_(tuple, index);
+    }
+}
+
+static inline PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, 
Py_ssize_t index)
+{
+    PyObject *result = PyTuple_GET_ITEM(tuple, index);
+    Py_INCREF(result);
+    return result;
+}
+
+static inline void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t 
index, PyObject *value)
+{
+    PyTuple_SET_ITEM(tuple, index, value);
+}
 
 
 // Exception operations
@@ -964,7 +1037,8 @@
 
 PyObject *CPy_CalculateMetaclass(PyObject *type, PyObject *o);
 PyObject *CPy_GetCoro(PyObject *obj);
-PyObject *CPyIter_Send(PyObject *iter, PyObject *val);
+PyObject *CPyGen_AlreadyExecutingError(int is_coroutine);
+PyObject *CPyIter_Send(PyObject *iter, PyObject *val, PyObject 
**stop_iter_value);
 int CPy_YieldFromErrorHandle(PyObject *iter, PyObject **outp);
 PyObject *CPy_FetchStopIterationValue(void);
 PyObject *CPyType_FromTemplate(PyObject *template_,
@@ -1007,18 +1081,24 @@
 PyObject *CPy_CallReverseOpMethod(PyObject *left, PyObject *right, const char 
*op,
                                   PyObject *method);
 
-bool CPyImport_ImportMany(PyObject *modules, CPyModule **statics[], PyObject 
*globals,
+bool CPyImport_ImportMany(PyObject *modules, CPyModuleCache *statics[], 
PyObject *globals,
                           PyObject *tb_path, PyObject *tb_function, Py_ssize_t 
*tb_lines);
 PyObject *CPyImport_ImportFromMany(PyObject *mod_id, PyObject *names, PyObject 
*as_names,
                                    PyObject *globals);
-PyObject *CPyImport_GetNativeAttrs(PyObject *mod_id, PyObject *names, PyObject 
*as_names,
+PyObject *CPyImport_GetNativeAttrs(PyObject *mod, PyObject *names, PyObject 
*as_names,
                                    PyObject *globals);
 PyObject *CPyImport_ImportNative(PyObject *module_name,
                                  PyObject *(*init_only_fn)(void),
                                  int (*exec_fn)(PyObject *),
                                  CPyModule **module_static,
+                                 CPyModuleCache *module_cache,
+                                 CPyImportState *state, CPyModuleLockAPI 
*lock_api,
                                  PyObject *shared_lib_file, PyObject 
*ext_suffix,
                                  Py_ssize_t is_package);
+int CPyImport_Exec(PyObject *module, int (*exec_fn)(PyObject *), 
CPyImportState *state,
+                   CPyModuleCache *module_cache);
+PyObject *CPyImport_BeginInitializing(PyObject *module);
+int CPyImport_EndInitializing(PyObject *spec);
 int CPyImport_SetDunderAttrs(PyObject *module, PyObject *module_name, PyObject 
*shared_lib_file,
                              PyObject *ext_suffix, Py_ssize_t is_package);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/PKG-INFO new/librt-0.16.0/PKG-INFO
--- old/librt-0.15.0/PKG-INFO   2026-08-07 12:12:21.037635300 +0200
+++ new/librt-0.16.0/PKG-INFO   2026-09-29 01:22:43.907548000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: librt
-Version: 0.15.0
+Version: 0.16.0
 Summary: Mypyc runtime library
 Author-email: Jukka Lehtosalo <[email protected]>, Ivan Levkivskyi 
<[email protected]>
 License-Expression: MIT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/bytearray_extra_ops.c 
new/librt-0.16.0/bytearray_extra_ops.c
--- old/librt-0.15.0/bytearray_extra_ops.c      2026-08-07 12:12:17.000000000 
+0200
+++ new/librt-0.16.0/bytearray_extra_ops.c      2026-09-29 01:22:41.000000000 
+0200
@@ -3,3 +3,31 @@
 PyObject *CPyByteArray_New(void) {
     return PyByteArray_FromStringAndSize(NULL, 0);
 }
+
+PyObject *CPyByteArray_FromBytesSlice(PyObject *obj, CPyTagged start, 
CPyTagged end) {
+    if (PyBytes_CheckExact(obj)
+            && (start == CPY_INT_TAG || CPyTagged_CheckShort(start))
+            && (end == CPY_INT_TAG || CPyTagged_CheckShort(end))) {
+        Py_ssize_t size = PyBytes_GET_SIZE(obj);
+        Py_ssize_t startn = start == CPY_INT_TAG ? 0 : 
CPyTagged_ShortAsSsize_t(start);
+        Py_ssize_t endn = end == CPY_INT_TAG ? size : 
CPyTagged_ShortAsSsize_t(end);
+        if (startn < 0) {
+            startn += size;
+        }
+        if (endn < 0) {
+            endn += size;
+        }
+        if (0 <= startn && startn <= endn && endn <= size) {
+            return PyByteArray_FromStringAndSize(PyBytes_AS_STRING(obj) + 
startn, endn - startn);
+        }
+    }
+
+    // Preserve general slice semantics, including bytes subclass overrides.
+    PyObject *slice = CPyObject_GetSlice(obj, start, end);
+    if (slice == NULL) {
+        return NULL;
+    }
+    PyObject *result = PyByteArray_FromObject(slice);
+    Py_DECREF(slice);
+    return result;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/bytearray_extra_ops.h 
new/librt-0.16.0/bytearray_extra_ops.h
--- old/librt-0.15.0/bytearray_extra_ops.h      2026-08-07 12:12:17.000000000 
+0200
+++ new/librt-0.16.0/bytearray_extra_ops.h      2026-09-29 01:22:41.000000000 
+0200
@@ -7,4 +7,8 @@
 // Construct empty bytearray
 PyObject *CPyByteArray_New(void);
 
+// Construct a bytearray from a bytes slice, avoiding an intermediate bytes 
object.
+// An omitted bound is represented by CPY_INT_TAG.
+PyObject *CPyByteArray_FromBytesSlice(PyObject *obj, CPyTagged start, 
CPyTagged end);
+
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/generic_ops.c 
new/librt-0.16.0/generic_ops.c
--- old/librt-0.15.0/generic_ops.c      2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/generic_ops.c      2026-09-29 01:22:41.000000000 +0200
@@ -47,9 +47,11 @@
 }
 
 PyObject *CPyObject_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) {
-    PyObject *start_obj = CPyTagged_AsObject(start);
-    PyObject *end_obj = CPyTagged_AsObject(end);
+    PyObject *start_obj = start == CPY_INT_TAG ? Py_NewRef(Py_None) : 
CPyTagged_AsObject(start);
+    PyObject *end_obj = end == CPY_INT_TAG ? Py_NewRef(Py_None) : 
CPyTagged_AsObject(end);
     if (unlikely(start_obj == NULL || end_obj == NULL)) {
+        Py_XDECREF(start_obj);
+        Py_XDECREF(end_obj);
         return NULL;
     }
     PyObject *slice = PySlice_New(start_obj, end_obj, NULL);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/librt.egg-info/PKG-INFO 
new/librt-0.16.0/librt.egg-info/PKG-INFO
--- old/librt-0.15.0/librt.egg-info/PKG-INFO    2026-08-07 12:12:20.000000000 
+0200
+++ new/librt-0.16.0/librt.egg-info/PKG-INFO    2026-09-29 01:22:43.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: librt
-Version: 0.15.0
+Version: 0.16.0
 Summary: Mypyc runtime library
 Author-email: Jukka Lehtosalo <[email protected]>, Ivan Levkivskyi 
<[email protected]>
 License-Expression: MIT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/librt.egg-info/SOURCES.txt 
new/librt-0.16.0/librt.egg-info/SOURCES.txt
--- old/librt-0.15.0/librt.egg-info/SOURCES.txt 2026-08-07 12:12:20.000000000 
+0200
+++ new/librt-0.16.0/librt.egg-info/SOURCES.txt 2026-09-29 01:22:43.000000000 
+0200
@@ -20,6 +20,7 @@
 init.c
 int_ops.c
 list_ops.c
+locks.c
 misc_ops.c
 mypyc_util.h
 pyproject.toml
@@ -57,6 +58,7 @@
 ./init.c
 ./int_ops.c
 ./list_ops.c
+./locks.c
 ./misc_ops.c
 ./mypyc_util.h
 ./pythoncapi_compat.h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/list_ops.c new/librt-0.16.0/list_ops.c
--- old/librt-0.15.0/list_ops.c 2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/list_ops.c 2026-09-29 01:22:41.000000000 +0200
@@ -283,7 +283,7 @@
     Py_ssize_t index = PyList_GET_SIZE(list) - 1;
     return list_pop_index(list, index);
 #else
-    // I tried a specalized version of pop_impl for just removing the
+    // I tried a specialized version of pop_impl for just removing the
     // last element and it wasn't any faster in microbenchmarks than
     // the generic one so I ditched it.
     return list_pop_impl((PyListObject *)list, -1);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/locks.c new/librt-0.16.0/locks.c
--- old/librt-0.15.0/locks.c    1970-01-01 01:00:00.000000000 +0100
+++ new/librt-0.16.0/locks.c    2026-09-29 01:22:41.000000000 +0200
@@ -0,0 +1,268 @@
+#include "CPy.h"
+
+struct CPyModuleLockAPI {
+    PyObject *get_module_lock;
+    PyObject *deadlock_error;
+};
+
+CPyModuleLockAPI *CPyModuleLockAPI_Alloc(void) {
+    CPyModuleLockAPI *api = PyMem_Calloc(1, sizeof(CPyModuleLockAPI));
+    if (api == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    PyObject *bootstrap = PyImport_ImportModule("importlib._bootstrap");
+    if (bootstrap == NULL) {
+        PyMem_Free(api);
+        return NULL;
+    }
+    api->get_module_lock = PyObject_GetAttrString(bootstrap, 
"_get_module_lock");
+    api->deadlock_error = PyObject_GetAttrString(bootstrap, "_DeadlockError");
+    Py_DECREF(bootstrap);
+    if (api->get_module_lock == NULL || api->deadlock_error == NULL) {
+        CPyModuleLockAPI_Free(api);
+        return NULL;
+    }
+    return api;
+}
+
+void CPyModuleLockAPI_Free(CPyModuleLockAPI *api) {
+    if (api == NULL) {
+        return;
+    }
+    Py_XDECREF(api->get_module_lock);
+    Py_XDECREF(api->deadlock_error);
+    PyMem_Free(api);
+}
+
+int CPyImport_AcquireLock(CPyModuleLockAPI *api, PyObject *module_name,
+                          PyObject **acquired_lock) {
+    *acquired_lock = NULL;
+    if (api == NULL) {
+        return CPY_LOCK_ACQUIRED;
+    }
+
+    PyObject *module_lock = PyObject_CallOneArg(api->get_module_lock, 
module_name);
+    if (module_lock == NULL) {
+        return CPY_LOCK_ERROR;
+    }
+    PyObject *result = PyObject_CallMethod(module_lock, "acquire", NULL);
+    if (result == NULL) {
+        if (PyErr_ExceptionMatches(api->deadlock_error)) {
+            PyErr_Clear();
+            Py_DECREF(module_lock);
+            return CPY_LOCK_DEADLOCK;
+        }
+        Py_DECREF(module_lock);
+        return CPY_LOCK_ERROR;
+    }
+    Py_DECREF(result);
+    *acquired_lock = module_lock;
+    return CPY_LOCK_ACQUIRED;
+}
+
+int CPyImport_ReleaseLock(PyObject *module_lock) {
+    if (module_lock == NULL) {
+        return 0;
+    }
+
+    PyObject *result = PyObject_CallMethod(module_lock, "release", NULL);
+    Py_DECREF(module_lock);
+    if (result == NULL) {
+        return -1;
+    }
+    Py_DECREF(result);
+    return 0;
+}
+
+// CPython exposes pyatomic.h through Python.h starting in 3.13. Older 
supported
+// versions are GIL-only, so plain accesses provide the same serialization 
there.
+bool CPyImport_IsInitialized(const CPyImportState *state) {
+#if PY_VERSION_HEX >= 0x030D0000
+    return _Py_atomic_load_int32(&state->initialized) != 0;
+#else
+    return state->initialized != 0;
+#endif
+}
+
+void CPyImport_SetInitialized(CPyImportState *state, bool initialized) {
+#if PY_VERSION_HEX >= 0x030D0000
+    _Py_atomic_store_int32(&state->initialized, initialized);
+#else
+    state->initialized = initialized;
+#endif
+}
+
+bool CPyImport_IsExecuted(const CPyImportState *state) {
+#if PY_VERSION_HEX >= 0x030D0000
+    return _Py_atomic_load_int32(&state->executed) != 0;
+#else
+    return state->executed != 0;
+#endif
+}
+
+void CPyImport_SetExecuted(CPyImportState *state) {
+#if PY_VERSION_HEX >= 0x030D0000
+    _Py_atomic_store_int32(&state->executed, true);
+#else
+    state->executed = true;
+#endif
+}
+
+static PyObject *CPyImport_DecodeModuleCache(CPyModuleCache cached) {
+    return (PyObject *)(cached & ~CPY_MODULE_CACHE_UNVERIFIED);
+}
+
+static CPyModuleCache CPyImport_EncodeModuleCache(PyObject *module, bool 
unverified) {
+    CPyModuleCache cached = (CPyModuleCache)module;
+    assert((cached & CPY_MODULE_CACHE_UNVERIFIED) == 0);
+    return cached | (unverified ? CPY_MODULE_CACHE_UNVERIFIED : 0);
+}
+
+static void CPyImport_DecRefOld(PyObject *previous) {
+    if (previous == NULL) {
+        return;
+    }
+#ifdef Py_GIL_DISABLED
+    // Atomic loads return borrowed references, so defer releasing the old
+    // reference until concurrent readers have passed a quiescent point.
+    if (!_Py_IsImmortal(previous)) {
+        _PyObject_XDecRefDelayed(previous);
+    }
+#else
+    Py_DECREF(previous);
+#endif
+}
+
+bool CPyImport_IsModuleInitializing(PyObject *module) {
+    PyObject *spec = PyObject_GetAttrString(module, "__spec__");
+    if (spec == NULL) {
+        PyErr_Clear();
+        return false;
+    }
+    PyObject *initializing = PyObject_GetAttrString(spec, "_initializing");
+    Py_DECREF(spec);
+    if (initializing == NULL) {
+        PyErr_Clear();
+        return false;
+    }
+    bool result = Py_IsTrue(initializing);
+    Py_DECREF(initializing);
+    return result;
+}
+
+PyObject *CPyImport_GetModuleCacheForImport(CPyModuleCache *cache, PyObject 
*module_name) {
+    for (;;) {
+        CPyModuleCache cached_value = CPyImport_LoadModuleCache(cache);
+        PyObject *cached = CPyImport_DecodeModuleCache(cached_value);
+        if (cached == Py_None) {
+            return Py_None;
+        }
+
+        // An untagged module has already been verified. This is the 
steady-state
+        // fast path and avoids consulting sys.modules or the module spec 
again.
+        if ((cached_value & CPY_MODULE_CACHE_UNVERIFIED) == 0) {
+            return cached;
+        }
+
+        // Generic imports may cache a partial module so compiled references 
after
+        // the import can use the value returned by CPython to break an 
import-lock
+        // deadlock. Such a module must not take the fast path on a later 
import.
+        // Comparing with sys.modules also rejects a partial module left 
behind in
+        // this cache after its initialization failed.
+        PyObject *current = PyImport_GetModule(module_name);
+        if (current == NULL) {
+            return PyErr_Occurred() ? NULL : Py_None;
+        }
+        bool valid = current == cached && 
!CPyImport_IsModuleInitializing(current);
+        Py_DECREF(current);
+        if (!valid) {
+            return Py_None;
+        }
+
+        // Clearing the tag changes only the cache metadata and does not 
transfer
+        // a reference. Retry if another thread replaced the cache: generated 
code
+        // reloads the cache after this check, so only the value whose tag we
+        // successfully cleared may take the fast path.
+#if PY_VERSION_HEX >= 0x030D0000
+        CPyModuleCache expected = cached_value;
+        if (_Py_atomic_compare_exchange_uintptr(cache, &expected, 
(CPyModuleCache)cached)) {
+            return cached;
+        }
+#else
+        if (*cache == cached_value) {
+            *cache = (CPyModuleCache)cached;
+            return cached;
+        }
+#endif
+    }
+}
+
+static void CPyImport_ReplaceModuleCacheValue(CPyModuleCache *cache, PyObject 
*module,
+                                              bool unverified) {
+    CPyModuleCache desired = CPyImport_EncodeModuleCache(module, unverified);
+#if PY_VERSION_HEX >= 0x030D0000
+    for (;;) {
+        CPyModuleCache previous = CPyImport_LoadModuleCache(cache);
+        if (previous == desired) {
+            return;
+        }
+        CPyModuleCache expected = previous;
+        PyObject *previous_module = CPyImport_DecodeModuleCache(previous);
+        if (previous_module == module) {
+            // Verification is monotonic for a particular module object. A
+            // thread that observed it while it was still initializing must not
+            // re-tag it after another thread has verified completion.
+            if ((previous & CPY_MODULE_CACHE_UNVERIFIED) == 0) {
+                return;
+            }
+            // Only the tag is changing, so the cache retains its existing
+            // reference to module.
+            if (_Py_atomic_compare_exchange_uintptr(cache, &expected, 
desired)) {
+                return;
+            }
+        } else {
+            Py_INCREF(module);
+            if (_Py_atomic_compare_exchange_uintptr(cache, &expected, 
desired)) {
+                if (previous_module != NULL && previous_module != Py_None) {
+                    CPyImport_DecRefOld(previous_module);
+                }
+                return;
+            }
+            Py_DECREF(module);
+        }
+    }
+#else
+    CPyModuleCache previous = *cache;
+    PyObject *previous_module = CPyImport_DecodeModuleCache(previous);
+    if (previous_module == module) {
+        if ((previous & CPY_MODULE_CACHE_UNVERIFIED) != 0) {
+            *cache = desired;
+        }
+        return;
+    }
+    Py_INCREF(module);
+    *cache = desired;
+    if (previous_module == NULL || previous_module == Py_None) {
+        return;
+    }
+    CPyImport_DecRefOld(previous_module);
+#endif
+}
+
+void CPyImport_ReplaceModuleCache(CPyModuleCache *cache, PyObject *module) {
+    CPyImport_ReplaceModuleCacheValue(cache, module, false);
+}
+
+void CPyImport_ReplaceModuleCacheUnverified(CPyModuleCache *cache, PyObject 
*module) {
+    CPyImport_ReplaceModuleCacheValue(cache, module, true);
+}
+
+void CPyImport_ReplaceModuleCacheForImport(CPyModuleCache *cache, PyObject 
*module) {
+    if (CPyImport_LoadModuleCache(cache) == (CPyModuleCache)module) {
+        return;
+    }
+
+    CPyImport_ReplaceModuleCacheValue(cache, module, 
CPyImport_IsModuleInitializing(module));
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/misc_ops.c new/librt-0.16.0/misc_ops.c
--- old/librt-0.15.0/misc_ops.c 2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/misc_ops.c 2026-09-29 01:22:41.000000000 +0200
@@ -22,14 +22,33 @@
     }
 }
 
-PyObject *CPyIter_Send(PyObject *iter, PyObject *val)
+// Raise CPython-compatible errors after a failed CPyGen_TryEnter.
+PyObject *CPyGen_AlreadyExecutingError(int is_coroutine)
+{
+    PyErr_SetString(PyExc_ValueError,
+                    is_coroutine ? "coroutine already executing"
+                                 : "generator already executing");
+    return NULL;
+}
+
+PyObject *CPyIter_Send(PyObject *iter, PyObject *val, PyObject 
**stop_iter_value)
 {
     // Do a send, or a next if second arg is None.
     // (This behavior is to match the PEP 380 spec for yield from.)
-    if (Py_IsNone(val)) {
-        return CPyIter_Next(iter);
-    } else {
-        return PyObject_CallMethodOneArg(iter, mypyc_interned_str.send, val);
+    //
+    // If the iterator yields a value, return it. If it completes normally,
+    // return NULL and store the return value in *stop_iter_value, without
+    // raising StopIteration. If it raises, return NULL and leave
+    // *stop_iter_value untouched (the caller initializes it to NULL).
+    PyObject *result;
+    switch (PyIter_Send(iter, val, &result)) {
+        case PYGEN_NEXT:
+            return result;
+        case PYGEN_RETURN:
+            *stop_iter_value = result;
+            return NULL;
+        default:
+            return NULL;
     }
 }
 
@@ -770,14 +789,19 @@
     return result;
 }
 
-static bool import_single(PyObject *mod_id, PyObject **mod_static,
+static bool import_single(PyObject *mod_id, CPyModuleCache *mod_static,
                           PyObject *globals_id, PyObject *globals_name, 
PyObject *globals) {
-    if (Py_IsNone(*mod_static)) {
+    PyObject *cached = CPyImport_GetModuleCacheForImport(mod_static, mod_id);
+    if (cached == NULL) {
+        return false;
+    }
+    if (Py_IsNone(cached)) {
         CPyModule *mod = PyImport_Import(mod_id);
         if (mod == NULL) {
             return false;
         }
-        *mod_static = mod;
+        CPyImport_ReplaceModuleCacheForImport(mod_static, mod);
+        Py_DECREF(mod);
     }
 
     PyObject *mod_dict = PyImport_GetModuleDict();
@@ -795,7 +819,7 @@
 }
 
 // Table-driven import helper. See transform_import() in irbuild for the 
details.
-bool CPyImport_ImportMany(PyObject *modules, CPyModule **statics[], PyObject 
*globals,
+bool CPyImport_ImportMany(PyObject *modules, CPyModuleCache *statics[], 
PyObject *globals,
                           PyObject *tb_path, PyObject *tb_function, Py_ssize_t 
*tb_lines) {
     for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(modules); i++) {
         PyObject *module = PyTuple_GET_ITEM(modules, i);
@@ -882,33 +906,24 @@
     return mod;
 }
 
-// Import attributes from an already-imported native module and store them
+// Import attributes from an already-imported native module object and store 
them
 // in the globals dict.  Returns the module on success, NULL on error.
-PyObject *CPyImport_GetNativeAttrs(PyObject *mod_id, PyObject *names,
+PyObject *CPyImport_GetNativeAttrs(PyObject *mod, PyObject *names,
                                    PyObject *as_names, PyObject *globals) {
-    PyObject *mod = PyImport_GetModule(mod_id);
-    if (mod == NULL) {
-        if (!PyErr_Occurred()) {
-            PyErr_Format(PyExc_ImportError, "module '%U' is not in 
sys.modules", mod_id);
-        }
-        return NULL;
-    }
     for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(names); i++) {
         PyObject *name = PyTuple_GET_ITEM(names, i);
         PyObject *as_name = PyTuple_GET_ITEM(as_names, i);
         PyObject *obj = PyObject_GetAttr(mod, name);
         if (obj == NULL) {
-            Py_DECREF(mod);
             return NULL;
         }
         int ret = CPyDict_SetItem(globals, as_name, obj);
         Py_DECREF(obj);
         if (ret < 0) {
-            Py_DECREF(mod);
             return NULL;
         }
     }
-    return mod;
+    return Py_NewRef(mod);
 }
 
 // From CPython
@@ -1506,46 +1521,244 @@
     return 0;
 }
 
+// Mark module's current spec as initializing and return an owned reference.
+PyObject *CPyImport_BeginInitializing(PyObject *module) {
+    PyObject *spec = PyObject_GetAttrString(module, "__spec__");
+    if (spec == NULL) {
+        return NULL;
+    }
+    if (PyObject_SetAttrString(spec, "_initializing", Py_True) < 0) {
+        Py_DECREF(spec);
+        return NULL;
+    }
+    return spec;
+}
+
+// Clear initializing on spec and consume its reference.
+int CPyImport_EndInitializing(PyObject *spec) {
+    int result = PyObject_SetAttrString(spec, "_initializing", Py_False);
+    Py_DECREF(spec);
+    return result;
+}
+
+// Split module_name into owned parent and child-name references.
+// Both outputs are NULL for a top-level module.
+static int CPyImport_SplitName(PyObject *module_name, PyObject **parent_name,
+                               PyObject **child_name) {
+    *parent_name = NULL;
+    *child_name = NULL;
+    Py_ssize_t name_len = PyUnicode_GetLength(module_name);
+    if (name_len < 0) {
+        return -1;
+    }
+    Py_ssize_t dot = PyUnicode_FindChar(module_name, '.', 0, name_len, -1);
+    if (dot < 0) {
+        return 0;
+    }
+    *parent_name = PyUnicode_Substring(module_name, 0, dot);
+    *child_name = PyUnicode_Substring(module_name, dot + 1, name_len);
+    if (*parent_name == NULL || *child_name == NULL) {
+        Py_CLEAR(*parent_name);
+        Py_CLEAR(*child_name);
+        return -1;
+    }
+    return 0;
+}
+
+// Import module_name's parent, if any.
+static int CPyImport_ImportParent(PyObject *module_name) {
+    PyObject *parent_name;
+    PyObject *child_name;
+    if (CPyImport_SplitName(module_name, &parent_name, &child_name) < 0) {
+        return -1;
+    }
+    Py_XDECREF(child_name);
+    if (parent_name == NULL) {
+        return 0;
+    }
+    PyObject *parent_module = PyImport_Import(parent_name);
+    Py_DECREF(parent_name);
+    if (parent_module == NULL) {
+        return -1;
+    }
+    Py_DECREF(parent_module);
+    return 0;
+}
+
+// Bind the module under its child name on the current parent in sys.modules.
+static int CPyImport_SetParentAttr(PyObject *module, PyObject *module_name) {
+    PyObject *parent_name;
+    PyObject *child_name;
+    if (CPyImport_SplitName(module_name, &parent_name, &child_name) < 0) {
+        return -1;
+    }
+    if (parent_name == NULL) {
+        return 0;
+    }
+    PyObject *parent_module = PyObject_GetItem(PyImport_GetModuleDict(), 
parent_name);
+    if (parent_module == NULL) {
+        Py_DECREF(parent_name);
+        Py_DECREF(child_name);
+        return -1;
+    }
+    int result = PyObject_SetAttr(parent_module, child_name, module);
+    Py_DECREF(parent_module);
+    if (result < 0 && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+        PyErr_Clear();
+        result = PyErr_WarnFormat(
+            PyExc_ImportWarning, 1,
+            "Cannot set an attribute on %R for child module %R",
+            parent_name, child_name);
+    }
+    Py_DECREF(parent_name);
+    Py_DECREF(child_name);
+    return result;
+}
+
+// Check whether a dotted module is already bound to its parent. A top-level
+// module has no parent binding to wait for.
+static int CPyImport_IsBoundToParent(PyObject *module, PyObject *module_name) {
+    PyObject *parent_name;
+    PyObject *child_name;
+    if (CPyImport_SplitName(module_name, &parent_name, &child_name) < 0) {
+        return -1;
+    }
+    if (parent_name == NULL) {
+        return 1;
+    }
+    PyObject *parent_module = PyObject_GetItem(PyImport_GetModuleDict(), 
parent_name);
+    Py_DECREF(parent_name);
+    if (parent_module == NULL) {
+        Py_DECREF(child_name);
+        return -1;
+    }
+    PyObject *child = PyObject_GetAttr(parent_module, child_name);
+    Py_DECREF(parent_module);
+    Py_DECREF(child_name);
+    if (child == NULL) {
+        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+            PyErr_Clear();
+            return 0;
+        }
+        return -1;
+    }
+    int result = child == module;
+    Py_DECREF(child);
+    return result;
+}
+
+static int CPyImport_ReleaseLockPreservingException(PyObject *module_lock) {
+    PyObject *exc_type, *exc_val, *exc_tb;
+    PyErr_Fetch(&exc_type, &exc_val, &exc_tb);
+    int result = CPyImport_ReleaseLock(module_lock);
+    if (result < 0) {
+        PyErr_Clear();
+    }
+    PyErr_Restore(exc_type, exc_val, exc_tb);
+    return result;
+}
+
+static void CPyImport_SetModuleReplacedError(PyObject *module_name) {
+    PyErr_Format(PyExc_ImportError,
+                 "native module '%U' in sys.modules was replaced after 
initialization",
+                 module_name);
+}
+
+static void CPyImport_SetModuleMissingError(PyObject *module_name) {
+    PyErr_Format(PyExc_ImportError,
+                 "initialized native module '%U' is missing from sys.modules",
+                 module_name);
+}
+
+// Execute a module once; caller holds the module lock.
+int CPyImport_Exec(PyObject *module, int (*exec_fn)(PyObject *), 
CPyImportState *state,
+                   CPyModuleCache *module_cache) {
+    if (CPyImport_IsExecuted(state)) {
+        const char *module_name = PyModule_GetName(module);
+        if (module_name != NULL) {
+            PyErr_Format(PyExc_ImportError,
+                         "native module '%s' does not support 
reinitialization",
+                         module_name);
+        }
+        return -1;
+    }
+
+    int result = exec_fn(module);
+    if (result == 0) {
+        PyObject *cached_module = CPyImport_GetModuleCache(module_cache);
+        // Keep the cache lazy. A normal shim import should not populate it, 
since
+        // the first compiled native import must still validate sys.modules. 
If a
+        // compiled import already populated the cache (including with a 
partial
+        // module during a circular import), refresh it to this instance but 
keep
+        // it tagged as unverified until import finalization has completed.
+        if (cached_module != NULL && cached_module != Py_None) {
+            CPyImport_ReplaceModuleCacheUnverified(module_cache, module);
+        }
+        // A successfully executed native module body must not run again, even 
if
+        // import finalization subsequently fails. Publish this only after any
+        // partial cache entry has been refreshed to the module that was 
executed.
+        CPyImport_SetExecuted(state);
+    }
+    return result;
+}
+
 PyObject *CPyImport_ImportNative(PyObject *module_name,
                                  PyObject *(*init_only_fn)(void),
                                  int (*exec_fn)(PyObject *),
                                  CPyModule **module_static,
+                                 CPyModuleCache *module_cache,
+                                 CPyImportState *state, CPyModuleLockAPI 
*lock_api,
                                  PyObject *shared_lib_file, PyObject 
*ext_suffix,
                                  Py_ssize_t is_package) {
-    PyObject *parent_module = NULL;
-    PyObject *child_name = NULL;
+    PyObject *initializing_spec = NULL;
     PyObject *exc_type, *exc_val, *exc_tb;
-    Py_ssize_t name_len = PyUnicode_GetLength(module_name);
-    if (name_len < 0) {
+    int end_result;
+
+    // Import the parent package first to preserve import ordering semantics.
+    if (CPyImport_ImportParent(module_name) < 0) {
         return NULL;
     }
-    Py_ssize_t dot = PyUnicode_FindChar(module_name, '.', 0, name_len, -1);
-    if (dot >= 0) {
-        // Import the parent package first to preserve import ordering 
semantics.
-        PyObject *parent_name = PyUnicode_Substring(module_name, 0, dot);
-        if (parent_name == NULL) {
-            CPyError_OutOfMemory();
-        }
-        child_name = PyUnicode_Substring(module_name, dot + 1, name_len);
-        if (child_name == NULL) {
-            CPyError_OutOfMemory();
-        }
-        parent_module = PyImport_Import(parent_name);
-        Py_DECREF(parent_name);
-        if (parent_module == NULL) {
-            Py_DECREF(child_name);
-            return NULL;
-        }
-    }
 
     // Create the module object without executing the module body.
     // CPyInitOnly_* uses an internal static to cache the module object.
     // We then check sys.modules to determine whether the module body
     // has already been executed (or is being executed in a circular import).
+    PyObject *module_lock;
+    int lock_result;
+    bool retried_after_execution = false;
+    while ((lock_result = CPyImport_AcquireLock(lock_api, module_name, 
&module_lock)) ==
+           CPY_LOCK_DEADLOCK) {
+        PyObject *partial = PyDict_GetItemWithError(PyImport_GetModuleDict(), 
module_name);
+        if (partial != NULL &&
+                (*module_static == NULL || partial == (PyObject 
*)*module_static)) {
+            CPyImport_ReplaceModuleCacheUnverified(module_cache, partial);
+            Py_INCREF(partial);
+            return partial;
+        }
+        // Multiple threads can detect the same cycle concurrently on a
+        // free-threaded build. If the target body has finished but the module
+        // is temporarily absent during importlib's pop/reinsert, retry once to
+        // wait for finalization. If it's still missing quit and report an 
error
+        // to not spin infinitely.
+        if (partial == NULL && !PyErr_Occurred() && 
CPyImport_IsExecuted(state) &&
+                !retried_after_execution) {
+            retried_after_execution = true;
+            continue;
+        }
+        if (!PyErr_Occurred()) {
+            PyErr_Format(PyExc_ImportError,
+                         "import deadlock for native module '%U' without a 
partial module",
+                         module_name);
+        }
+        return NULL;
+    }
+    if (lock_result == CPY_LOCK_ERROR) {
+        return NULL;
+    }
+
     PyObject *module_dict = PyImport_GetModuleDict();
     if (module_dict == NULL) {
-        Py_XDECREF(parent_module);
-        Py_XDECREF(child_name);
+        CPyImport_ReleaseLockPreservingException(module_lock);
         return NULL;
     }
 
@@ -1553,34 +1766,55 @@
     if (existing != NULL) {
         if (*module_static != NULL) {
             if (existing == (PyObject *)*module_static) {
+                // Acquiring the module lock synchronizes with a regular 
import.
+                // A recursive acquisition can still happen during 
finalization,
+                // so also verify the spec and parent binding before 
publishing.
+                if (CPyImport_IsExecuted(state)
+                        && !CPyImport_IsModuleInitializing(existing)) {
+                    int is_bound = CPyImport_IsBoundToParent(existing, 
module_name);
+                    if (is_bound < 0) {
+                        CPyImport_ReleaseLockPreservingException(module_lock);
+                        return NULL;
+                    }
+                    if (is_bound) {
+                        // Publish the verified cache value before the 
completion
+                        // flag. An acquire-load of initialized can then trust 
the
+                        // cached module without re-reading it for validation.
+                        CPyImport_ReplaceModuleCache(module_cache, existing);
+                        CPyImport_SetInitialized(state, true);
+                    }
+                }
+                if (!CPyImport_IsInitialized(state)) {
+                    CPyImport_ReplaceModuleCacheUnverified(module_cache, 
existing);
+                }
                 Py_INCREF(existing);
-                Py_XDECREF(parent_module);
-                Py_XDECREF(child_name);
+                if (CPyImport_ReleaseLock(module_lock) < 0) {
+                    Py_DECREF(existing);
+                    existing = NULL;
+                }
                 return existing;
             }
-            PyErr_Format(PyExc_ImportError,
-                         "native module '%U' in sys.modules was replaced after 
initialization",
-                         module_name);
-            Py_XDECREF(parent_module);
-            Py_XDECREF(child_name);
+            CPyImport_SetModuleReplacedError(module_name);
+            CPyImport_ReleaseLockPreservingException(module_lock);
             return NULL;
         }
     }
     if (PyErr_Occurred()) {
-        Py_XDECREF(parent_module);
-        Py_XDECREF(child_name);
+        CPyImport_ReleaseLockPreservingException(module_lock);
         return NULL;
     }
 
-    PyObject *modobj = init_only_fn();
-    if (modobj == NULL) {
-        Py_XDECREF(parent_module);
-        Py_XDECREF(child_name);
+    if (CPyImport_IsInitialized(state) ||
+            CPyImport_GetModuleCache(module_cache) == (PyObject 
*)*module_static) {
+        CPyImport_SetModuleMissingError(module_name);
+        CPyImport_ReleaseLockPreservingException(module_lock);
         return NULL;
     }
 
-    if (PyObject_SetItem(module_dict, module_name, modobj) < 0) {
-        goto fail;
+    PyObject *modobj = init_only_fn();
+    if (modobj == NULL) {
+        CPyImport_ReleaseLockPreservingException(module_lock);
+        return NULL;
     }
 
     if (*module_static != (CPyModule *)modobj) {
@@ -1594,19 +1828,61 @@
         goto fail;
     }
 
+    initializing_spec = CPyImport_BeginInitializing(modobj);
+    if (initializing_spec == NULL) {
+        goto fail;
+    }
+
+    if (PyObject_SetItem(module_dict, module_name, modobj) < 0) {
+        goto fail;
+    }
+
     // Now execute the module body, with __file__ and __package__ already set.
     if (exec_fn(modobj) != 0) {
         goto fail;
     }
 
-    // Match CPython import semantics: publish parent.child only after the
-    // child module finished executing successfully.
-    if (parent_module != NULL && PyObject_SetAttr(parent_module, child_name, 
modobj) < 0) {
+    // Direct imports bypass importlib's final sys.modules lookup. Reject 
changes
+    // to the entry instead of returning an object different from future 
imports.
+    PyObject *current = PyDict_GetItemWithError(module_dict, module_name);
+    if (current == NULL) {
+        if (!PyErr_Occurred()) {
+            CPyImport_SetModuleMissingError(module_name);
+        }
+        goto fail;
+    }
+    if (current != modobj) {
+        CPyImport_SetModuleReplacedError(module_name);
         goto fail;
     }
 
-    Py_XDECREF(parent_module);
-    Py_XDECREF(child_name);
+    end_result = CPyImport_EndInitializing(initializing_spec);
+    initializing_spec = NULL;
+    if (end_result < 0) {
+        goto fail;
+    }
+
+    // Direct imports must publish parent.child themselves; normal extension
+    // loading leaves this to importlib.
+    int parent_result = CPyImport_SetParentAttr(modobj, module_name);
+    // The module body and import finalization are complete even if publishing
+    // parent.child reports an error. Cache the verified object before setting
+    // the monotonic completion flag.
+    CPyImport_ReplaceModuleCache(module_cache, modobj);
+    CPyImport_SetInitialized(state, true);
+    if (parent_result < 0) {
+        // The module finished initializing before parent binding was 
attempted,
+        // so preserve it even if the warning is promoted to an exception or
+        // setting the attribute fails with an exception other than 
AttributeError.
+        CPyImport_ReleaseLockPreservingException(module_lock);
+        Py_DECREF(modobj);
+        return NULL;
+    }
+
+    if (CPyImport_ReleaseLock(module_lock) < 0) {
+        Py_DECREF(modobj);
+        return NULL;
+    }
     return modobj;
 
 fail:
@@ -1615,10 +1891,13 @@
     PyErr_Fetch(&exc_type, &exc_val, &exc_tb);
     PyObject_DelItem(module_dict, module_name);
     PyErr_Clear();
+    if (initializing_spec != NULL) {
+        CPyImport_EndInitializing(initializing_spec);
+        PyErr_Clear();
+    }
     PyErr_Restore(exc_type, exc_val, exc_tb);
-    Py_XDECREF(parent_module);
-    Py_XDECREF(child_name);
     Py_CLEAR(*module_static);
+    CPyImport_ReleaseLockPreservingException(module_lock);
     return NULL;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/mypyc_util.h 
new/librt-0.16.0/mypyc_util.h
--- old/librt-0.15.0/mypyc_util.h       2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/mypyc_util.h       2026-09-29 01:22:41.000000000 +0200
@@ -17,10 +17,13 @@
 
 #if defined(__clang__) || defined(__GNUC__)
 #define CPy_NOINLINE __attribute__((noinline))
+#define CPy_COLD __attribute__((cold))
 #elif defined(_MSC_VER)
 #define CPy_NOINLINE __declspec(noinline)
+#define CPy_COLD
 #else
 #define CPy_NOINLINE
+#define CPy_COLD
 #endif
 
 #ifndef Py_GIL_DISABLED
@@ -130,6 +133,11 @@
 
 typedef PyObject CPyModule;
 
+// Module caches use the low bit to distinguish a module that may still be
+// initializing from one whose initialization has been verified. PyObject
+// pointers are aligned, so the low bit is otherwise unused.
+typedef uintptr_t CPyModuleCache;
+
 // Tag bit used for long integers
 #define CPY_INT_TAG 1
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/pyproject.toml 
new/librt-0.16.0/pyproject.toml
--- old/librt-0.15.0/pyproject.toml     2026-08-07 12:12:09.000000000 +0200
+++ new/librt-0.16.0/pyproject.toml     2026-09-29 01:22:34.000000000 +0200
@@ -19,7 +19,7 @@
     {name = "Jukka Lehtosalo", email = "[email protected]"},
     {name = "Ivan Levkivskyi", email = "[email protected]"},
 ]
-version = "0.15.0"
+version = "0.16.0"
 license = "MIT"
 classifiers = [
   "Development Status :: 3 - Alpha",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/pythonsupport.c 
new/librt-0.16.0/pythonsupport.c
--- old/librt-0.15.0/pythonsupport.c    2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/pythonsupport.c    2026-09-29 01:22:41.000000000 +0200
@@ -6,23 +6,31 @@
 #include "pythonsupport.h"
 
 #ifdef Py_GIL_DISABLED
-// Cold slow path of CPy_GetAttrRef (declared in pythonsupport.h). Reached only
-// when the inline fast-path try-incref fails: the value is owned by another
-// thread, so taking a reference requires an atomic shared-refcount operation.
-// Kept out-of-line so the inline fast path stays small.
+// Cold slow path of CPy_GetAttrRef (declared in pythonsupport.h). First try to
+// acquire a shared reference without locking, then validate that the field 
still
+// contains v. If validation fails, drop the provisional reference. If the 
shared
+// incref or validation fails, take the owner's critical section, reload the 
field,
+// and take a reference while the value cannot be replaced. _Py_XNewRefWithLock
+// also sets maybe-weakref lazily, so later cross-thread reads generally use 
the
+// lock-free shared-refcount path. Returns NULL if the field is NULL on reload.
 //
-// First try the lock-free shared-refcount CAS. If the value has not had
-// maybe-weakref set yet (for example, it was published by CPy_InitAttrRef), 
force
-// a cross-thread reference via _Py_NewRefWithLock, which cannot fail and sets
-// maybe-weakref so subsequent reads take the fast path. The value was already
-// observed in the field by CPy_GetAttrRef; CPy_SetAttrRef's QSBR-delayed 
decref
-// keeps any replaced value alive long enough for this reader.
+// The reload can be relaxed: acquiring the owner's critical section 
synchronizes
+// with the writer ending its critical section. A value stored by 
CPy_InitAttrRef is
+// ordered by the later publication of the owner itself (see CPy_InitAttrRef).
 CPy_NOINLINE
-PyObject *CPy_GetAttrRefSlow(PyObject *v) {
+CPy_COLD
+PyObject *CPy_GetAttrRefSlow(PyObject *v, PyObject *owner, PyObject **field) {
     if (_Py_TryIncRefShared(v)) {
-        return v;
+        if (v == (PyObject *)_Py_atomic_load_ptr(field)) {
+            return v;
+        }
+        Py_DECREF(v);
     }
-    return _Py_NewRefWithLock(v);  // sets maybe-weakref; cannot fail
+    PyObject *result;
+    Py_BEGIN_CRITICAL_SECTION(owner);
+    result = _Py_XNewRefWithLock((PyObject 
*)_Py_atomic_load_ptr_relaxed(field));
+    Py_END_CRITICAL_SECTION();
+    return result;
 }
 #endif
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/pythonsupport.h 
new/librt-0.16.0/pythonsupport.h
--- old/librt-0.15.0/pythonsupport.h    2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/pythonsupport.h    2026-09-29 01:22:41.000000000 +0200
@@ -44,30 +44,43 @@
 //
 // On free-threaded builds a plain load followed by an incref races with a
 // concurrent setter that may decref the old value to zero and free it before 
the
-// incref runs (use-after-free). CPy_SetAttrRef avoids that by reclaiming old
-// values through QSBR-delayed decref, so a value observed in the field remains
-// safe to touch while this reader is running.
-//
-// Only the hot case is inlined here: an incref of a value owned by this 
thread or
-// immortal, via '_Py_TryIncrefFast' (no CAS, no loop). Everything colder -- 
the
-// cross-thread shared-refcount CAS and the _Py_NewRefWithLock fallback -- 
lives
-// out-of-line in 'CPy_GetAttrRefSlow'. Splitting it this way keeps each call
-// site's fast path small enough to inline, which is measurably faster than
-// letting the compiler auto-out-line the whole helper (that merges every read
-// site's branch history into one shared copy and mispredicts). It is only 
used in
-// free-threaded builds; the default (GIL) build keeps the plain load + incref
-// generated inline by mypyc.
-PyObject *CPy_GetAttrRefSlow(PyObject *v);
+// incref runs (use-after-free). We avoid that with an optimistic try-incref,
+// validating the field when taking a shared reference. If no reference can be
+// secured that way, the fallback takes the same per-object lock as 
CPy_SetAttrRef
+// and reloads the field before taking a reference. This mirrors CPython's own
+// instance attribute read (in CPython 3.14: _Py_TryIncrefCompare, then a 
locked
+// reload -- see _PyObject_TryGetInstanceAttribute in Objects/dictobject.c).
+//
+// CPython's QSBR-aware object heaps keep a stale pointer safe to inspect as a
+// PyObject header. The try-incref protocol ensures that stale memory is never 
returned
+// as the attribute value. These guarantees depend on CPython internals and 
must be
+// rechecked when adding support for a new Python version.
+//
+// The hot path is lock-free and intentionally small: _Py_TryIncrefFast handles
+// values owned by this thread and immortal values. Everything colder -- the
+// shared-refcount CAS, field validation, and per-object lock fallback -- is in
+// CPy_GetAttrRefSlow. The cold annotation also prevents those arguments and
+// branches from bloating the caller's hot path. An unflagged cross-thread 
value
+// takes the locked slow path once; _Py_XNewRefWithLock sets maybe-weakref 
lazily,
+// so subsequent reads generally succeed through the lock-free shared-refcount
+// path. The default (GIL) build keeps the plain load + incref generated 
inline by
+// mypyc.
+//
+// Note that observing a non-NULL 'v' does not guarantee a non-NULL result: 
'v' is
+// only a provisional read, and if the attribute is deleted before a reference 
to it
+// can be secured, this returns NULL and the caller raises AttributeError. 
That is a
+// legal outcome for a racing read, since it is ordered after the delete.
+CPy_COLD PyObject *CPy_GetAttrRefSlow(PyObject *v, PyObject *owner, PyObject 
**field);
 
-static inline PyObject *CPy_GetAttrRef(PyObject **field) {
+static inline PyObject *CPy_GetAttrRef(PyObject *owner, PyObject **field) {
     PyObject *v = (PyObject *)_Py_atomic_load_ptr_acquire(field);
     if (v == NULL) {
         return NULL;
     }
-    if (_Py_TryIncrefFast(v)) {
+    if (likely(_Py_TryIncrefFast(v))) {
         return v;
     }
-    return CPy_GetAttrRefSlow(v);
+    return CPy_GetAttrRefSlow(v, owner, field);
 }
 
 // Read a native attribute that is a single reference-counted 'PyObject *' 
field
@@ -78,10 +91,10 @@
 // use-after-free race that CPy_GetAttrRef guards against cannot happen: the 
field
 // holds a strong reference for the object's whole lifetime, and any thread 
reading
 // it necessarily holds 'self', which keeps the value alive. So the try-incref 
+
-// _Py_NewRefWithLock fallback are unnecessary here -- a plain load + 
Py_INCREF is
-// safe. A cross-thread Py_INCREF is an unconditional
-// atomic add on ob_ref_shared, so (unlike CPy_GetAttrRef's try-incref) it 
needs no
-// maybe-weakref and has no slow path. The load is relaxed rather than 
acquire: the
+// _Py_XNewRefWithLock fallback are unnecessary here -- a plain load + 
Py_INCREF is
+// safe. A cross-thread Py_INCREF is an unconditional atomic add on 
ob_ref_shared
+// (CPython 3.14), so (unlike CPy_GetAttrRef's try-incref) it needs no 
maybe-weakref
+// and has no slow path. The load is relaxed rather than acquire: the
 // reader reached 'self' through a synchronization edge (self's own 
publication)
 // that already ordered the construction stores before it, exactly as with
 // CPy_InitAttrRef's relaxed store. Relaxed keeps it TSan-clean at zero cost 
(plain
@@ -94,57 +107,39 @@
     return v;
 }
 
-// Reclaim the previous value of a native attribute after it has been replaced.
-//
-// CPy_GetAttrRef reads the field optimistically without holding any lock the
-// writer also takes, so a reader can load the old pointer and then try to 
take a
-// reference after this store. The old value must therefore stay alive until 
every
-// thread has passed a quiescent point, which is exactly what a QSBR-deferred
-// decref guarantees. So all mortal old values are
-// reclaimed via _PyObject_XDecRefDelayed, matching CPython's own 
replace-a-slot
-// paths (e.g. _PyObject_SetDict / _PyObject_SetManagedDict).
-//
-// We deliberately do NOT take a "local refcount > 1, owned by this thread" 
fast
-// path: dropping the field's reference is not the only decref of the object, 
so a
-// non-freeing local decrement here does not prevent an unrelated reference 
holder
-// from driving the object to zero (a plain, non-deferred Py_DECREF -> 
_Py_Dealloc)
-// while an in-flight reader still holds the stale pointer -- a use-after-free 
that
-// only QSBR deferral closes. Immortal objects are never freed, so skipping 
their
-// decref entirely is safe and avoids queuing a no-op onto the delayed-free 
list.
-static inline void CPy_DecRefAttrOld(PyObject *op) {
-    if (op == NULL) {
-        return;
-    }
-    if (_Py_IsImmortal(op)) {
-        return;
-    }
-    _PyObject_XDecRefDelayed(op);
-}
-
 // Set a native attribute that is a single reference-counted 'PyObject *' 
field,
 // stealing the reference to 'value' (which may be NULL to delete the 
attribute)
 // and safely reclaiming the previous value.
 //
-// Memory safety does NOT depend on SetMaybeWeakref here, so (unlike an earlier
-// version) we do not call it. Two things keep this safe:
-//   - The old value is reclaimed via CPy_DecRefAttrOld, a QSBR-deferred 
decref, so
-//     it cannot be freed while an in-flight CPy_GetAttrRef still holds the 
stale
-//     pointer.
-//   - A concurrent cross-thread reader whose inline fast-path try-incref 
fails on
-//     an unflagged 'value' still cannot fail and never blocks on this writer:
-//     CPy_GetAttrRef falls into CPy_GetAttrRefSlow, which is fully lock-free. 
It
-//     retries with a lock-free shared-refcount CAS (_Py_TryIncRefShared) and, 
only
-//     if that also fails, forces a reference via _Py_NewRefWithLock, which 
cannot
-//     fail and lazily sets maybe-weakref so later cross-thread reads take the 
CAS.
-// This mirrors CPy_InitAttrRef, which already omits SetMaybeWeakref for the 
same
-// reason. Setting the flag here would only be a possible performance tuning 
knob
-// (it would let that first cross-thread reader succeed on the cheaper
-// _Py_TryIncRefShared CAS instead of falling through to _Py_NewRefWithLock); 
it is
-// not needed for correctness. The atomic exchange publishes the new pointer 
and
-// hands back the old one without a writer/writer race.
-static inline void CPy_SetAttrRef(PyObject **field, PyObject *value) {
-    PyObject *old = (PyObject *)_Py_atomic_exchange_ptr(field, value);
-    CPy_DecRefAttrOld(old);
+// The owner's critical section serializes writers and synchronizes them with
+// CPy_GetAttrRef's fallback path. A lock-free reader either secures a local or
+// immortal reference directly, or secures a shared reference and validates the
+// field. Otherwise it reloads the field under this lock. Thus once the 
replacement
+// is published, the old field reference can be decrefed immediately, even if a
+// reader is still dereferencing the old value (see CPy_GetAttrRef for why 
touching
+// a freed value there is safe). Decref outside the critical section so an
+// arbitrary destructor does not run while the owner is locked -- the 
equivalent in
+// CPython 3.14 (store_instance_attr_lock_held) decrefs with the lock still 
held.
+static inline void CPy_SetAttrRef(PyObject *owner, PyObject **field, PyObject 
*value) {
+    PyObject *old;
+    Py_BEGIN_CRITICAL_SECTION(owner);
+    old = (PyObject *)_Py_atomic_load_ptr_relaxed(field);
+    _Py_atomic_store_ptr_release(field, value);
+    Py_END_CRITICAL_SECTION();
+    Py_XDECREF(old);
+}
+
+// Publish a lazily created attribute once. Concurrent initializers may create
+// separate values; only the first is installed, and the others are discarded.
+// Decref outside the owner's critical section in case it runs Python code.
+static inline void CPy_InitAttrRefIfNull(PyObject *owner, PyObject **field, 
PyObject *value) {
+    Py_BEGIN_CRITICAL_SECTION(owner);
+    if (_Py_atomic_load_ptr_relaxed(field) == NULL) {
+        _Py_atomic_store_ptr_release(field, value);
+        value = NULL;
+    }
+    Py_END_CRITICAL_SECTION();
+    Py_XDECREF(value);
 }
 
 // Initialize a native attribute that is known to be previously undefined 
(NULL),
@@ -158,14 +153,45 @@
 // returned), and that publication carries the release barrier making all the
 // construction stores visible. A relaxed store therefore suffices.
 //
-// Unlike CPy_SetAttrRef, this deliberately does NOT call SetMaybeWeakref (its 
CAS
-// is pure overhead here, ~+2.6ns per fresh store, and construction-heavy code
-// pays it on every attribute of every new object). The cost is moved off this 
hot
-// path onto CPy_GetAttrRef's cold slow path, which sets maybe-weakref lazily 
on
+// This deliberately does NOT call SetMaybeWeakref (its CAS is pure overhead 
here,
+// ~+2.6ns per fresh store, and construction-heavy code pays it on every 
attribute
+// of every new object). CPy_SetAttrRef likewise leaves the flag unset. The 
cost is
+// moved onto CPy_GetAttrRef's cold slow path, which sets maybe-weakref lazily 
on
 // the first cross-thread read that needs it.
 static inline void CPy_InitAttrRef(PyObject **field, PyObject *value) {
     _Py_atomic_store_ptr_relaxed(field, value);
 }
+
+#endif
+
+// Generated generator and coroutine helpers claim this flag while executing,
+// rejecting reentrant or concurrent resumes.
+//
+// On free-threaded builds, the atomic exchange provides mutual exclusion and 
acquire
+// ordering; the release store publishes body writes to the next resume. This 
also
+// permits plain access to private generator attributes. Claiming must be a 
single
+// atomic operation, or two threads could both observe a clear flag and enter. 
Under
+// the GIL, plain accesses suffice.
+#ifdef Py_GIL_DISABLED
+static inline int CPyGen_TryEnter(uint32_t *running) {
+    return _Py_atomic_exchange_uint32(running, 1) == 0;
+}
+
+static inline void CPyGen_Exit(uint32_t *running) {
+    _Py_atomic_store_uint32_release(running, 0);
+}
+#else
+static inline int CPyGen_TryEnter(uint32_t *running) {
+    if (*running) {
+        return 0;
+    }
+    *running = 1;
+    return 1;
+}
+
+static inline void CPyGen_Exit(uint32_t *running) {
+    *running = 0;
+}
 #endif
 
 PyObject* update_bases(PyObject *bases);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/strings/librt_strings.c 
new/librt-0.16.0/strings/librt_strings.c
--- old/librt-0.15.0/strings/librt_strings.c    2026-08-07 12:12:17.000000000 
+0200
+++ new/librt-0.16.0/strings/librt_strings.c    2026-09-29 01:22:41.000000000 
+0200
@@ -1358,6 +1358,8 @@
 PyMODINIT_FUNC
 PyInit_strings(void)
 {
-    intern_strings();
+    if (intern_strings() < 0) {
+        return NULL;
+    }
     return PyModuleDef_Init(&librt_strings_module);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/threading/librt_threading_api.h 
new/librt-0.16.0/threading/librt_threading_api.h
--- old/librt-0.15.0/threading/librt_threading_api.h    2026-08-07 
12:12:17.000000000 +0200
+++ new/librt-0.16.0/threading/librt_threading_api.h    2026-09-29 
01:22:41.000000000 +0200
@@ -1,6 +1,8 @@
 #ifndef LIBRT_THREADING_API_H
 #define LIBRT_THREADING_API_H
 
+#include <stdbool.h>
+
 #include "librt_threading.h"
 
 int
@@ -17,4 +19,8 @@
 #define LibRTThreading_Lock_locked_internal (*(char (*)(PyObject *self)) 
LibRTThreading_API[6])
 #define LibRTThreading_Lock_acquire_blocking_internal (*(char (*)(PyObject 
*self, char blocking)) LibRTThreading_API[7])
 
+static inline bool CPyLock_Check(PyObject *obj) {
+    return Py_TYPE(obj) == LibRTThreading_Lock_type_internal();
+}
+
 #endif  // LIBRT_THREADING_API_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/librt-0.15.0/tuple_ops.c new/librt-0.16.0/tuple_ops.c
--- old/librt-0.15.0/tuple_ops.c        2026-08-07 12:12:17.000000000 +0200
+++ new/librt-0.16.0/tuple_ops.c        2026-09-29 01:22:41.000000000 +0200
@@ -5,21 +5,16 @@
 #include <Python.h>
 #include "CPy.h"
 
-PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index) {
+PyObject *CPySequenceTuple_GetItem_(PyObject *tuple, CPyTagged index) {
     if (CPyTagged_CheckShort(index)) {
         Py_ssize_t n = CPyTagged_ShortAsSsize_t(index);
         Py_ssize_t size = PyTuple_GET_SIZE(tuple);
-        if (n >= 0) {
-            if (n >= size) {
-                PyErr_SetString(PyExc_IndexError, "tuple index out of range");
-                return NULL;
-            }
-        } else {
+        if (n < 0) {
             n += size;
-            if (n < 0) {
-                PyErr_SetString(PyExc_IndexError, "tuple index out of range");
-                return NULL;
-            }
+        }
+        if (n < 0 || n >= size) {
+            PyErr_SetString(PyExc_IndexError, "tuple index out of range");
+            return NULL;
         }
         PyObject *result = PyTuple_GET_ITEM(tuple, n);
         Py_INCREF(result);
@@ -45,18 +40,3 @@
     }
     return CPyObject_GetSlice(obj, start, end);
 }
-
-// No error checking
-PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index)
-{
-    PyObject *result = PyTuple_GET_ITEM(tuple, index);
-    Py_INCREF(result);
-    return result;
-}
-
-// PyTuple_SET_ITEM does no error checking,
-// and should only be used to fill in brand new tuples.
-void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, 
PyObject *value)
-{
-    PyTuple_SET_ITEM(tuple, index, value);
-}

Reply via email to