the outer function is never inlined
(&self)
| 1595 | |
| 1596 | #[inline(always)] // the outer function is never inlined |
| 1597 | fn drop_slow_inner(&self) -> Result<(), ()> { |
| 1598 | // __del__ is mostly not implemented |
| 1599 | #[inline(never)] |
| 1600 | #[cold] |
| 1601 | fn call_slot_del( |
| 1602 | zelf: &PyObject, |
| 1603 | slot_del: fn(&PyObject, &VirtualMachine) -> PyResult<()>, |
| 1604 | ) -> Result<(), ()> { |
| 1605 | let ret = crate::vm::thread::with_vm(zelf, |vm| { |
| 1606 | // Temporarily resurrect (0→2) so ref_count stays positive |
| 1607 | // during __del__, preventing safe_inc from seeing 0. |
| 1608 | zelf.0.ref_count.inc_by(2); |
| 1609 | |
| 1610 | if let Err(e) = slot_del(zelf, vm) { |
| 1611 | let del_method = zelf.get_class_attr(identifier!(vm, __del__)).unwrap(); |
| 1612 | vm.run_unraisable(e, None, del_method); |
| 1613 | } |
| 1614 | |
| 1615 | // Undo the temporary resurrection. Always remove both |
| 1616 | // temporary refs; the second dec returns true only when |
| 1617 | // ref_count drops to 0 (no resurrection). |
| 1618 | let _ = zelf.0.ref_count.dec(); |
| 1619 | zelf.0.ref_count.dec() |
| 1620 | }); |
| 1621 | match ret { |
| 1622 | // the decref set ref_count back to 0 |
| 1623 | Some(true) => Ok(()), |
| 1624 | // we've been resurrected by __del__ |
| 1625 | Some(false) => Err(()), |
| 1626 | None => Ok(()), |
| 1627 | } |
| 1628 | } |
| 1629 | |
| 1630 | // __del__ should only be called once (like _PyGC_FINALIZED check in GIL_DISABLED) |
| 1631 | // We call __del__ BEFORE clearing weakrefs to allow the finalizer to access |
| 1632 | // the object's weak references if needed. |
| 1633 | let del = self.class().slots.del.load(); |
| 1634 | if let Some(slot_del) = del |
| 1635 | && !self.gc_finalized() |
| 1636 | { |
| 1637 | self.set_gc_finalized(); |
| 1638 | call_slot_del(self, slot_del)?; |
| 1639 | } |
| 1640 | |
| 1641 | // Clear weak refs AFTER __del__. |
| 1642 | // Note: This differs from GC behavior which clears weakrefs before finalizers, |
| 1643 | // but for direct deallocation (drop_slow_inner), we need to allow the finalizer |
| 1644 | // to run without triggering use-after-free from WeakRefList operations. |
| 1645 | if let Some(wrl) = self.weak_ref_list() { |
| 1646 | wrl.clear(self); |
| 1647 | } |
| 1648 | |
| 1649 | Ok(()) |
| 1650 | } |
| 1651 | |
| 1652 | /// _Py_Dealloc: dispatch to type's dealloc |
| 1653 | #[inline(never)] |
no test coverage detected