diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs
index 64801a1de5..38b4bddd1f 100644
--- a/crates/vm/src/builtins/type.rs
+++ b/crates/vm/src/builtins/type.rs
@@ -18,7 +18,7 @@ use crate::{
common::{
ascii,
borrow::BorrowedValue,
- lock::{PyMutex, PyRwLock, PyRwLockReadGuard},
+ lock::{PyRwLock, PyRwLockReadGuard},
},
function::{FuncArgs, KwArgs, OptionalArg, PyMethodDef, PySetterValue},
object::{Traverse, TraverseFn},
@@ -216,6 +216,24 @@ pub fn type_cache_clear() {
TYPE_CACHE_CLEARING.store(false, Ordering::Release);
}
+/// Repair type-cache SeqLock state in the post-fork child.
+///
+/// If fork happens while a writer holds an entry SeqLock, the child inherits
+/// the odd sequence value with no surviving writer to release it. Clear only
+/// those in-progress entries, matching CPython's `_PyTypes_AfterFork()`.
+pub unsafe fn type_cache_after_fork() {
+ for entry in TYPE_CACHE.iter() {
+ let seq = entry.sequence.load(Ordering::Relaxed);
+ if (seq & 1) == 0 {
+ continue;
+ }
+ entry.value.store(core::ptr::null_mut(), Ordering::Relaxed);
+ entry.name.store(core::ptr::null_mut(), Ordering::Relaxed);
+ entry.version.store(0, Ordering::Relaxed);
+ entry.sequence.store(0, Ordering::Relaxed);
+ }
+}
+
unsafe impl crate::object::Traverse for PyType {
fn traverse(&self, tracer_fn: &mut crate::object::TraverseFn<'_>) {
self.base.traverse(tracer_fn);
@@ -276,8 +294,6 @@ pub struct TypeSpecializationCache {
pub init: PyAtomicRef