get_or_create_weakref
(
&self,
obj: &PyObject,
cls: PyTypeRef,
cls_is_weakref: bool,
callback: Option<PyObjectRef>,
dict: Option<PyDictRef>,
)
| 553 | |
| 554 | /// get_or_create_weakref |
| 555 | fn add( |
| 556 | &self, |
| 557 | obj: &PyObject, |
| 558 | cls: PyTypeRef, |
| 559 | cls_is_weakref: bool, |
| 560 | callback: Option<PyObjectRef>, |
| 561 | dict: Option<PyDictRef>, |
| 562 | ) -> PyRef<PyWeak> { |
| 563 | let is_generic = cls_is_weakref && callback.is_none(); |
| 564 | |
| 565 | // Try reuse under lock first (fast path, no allocation) |
| 566 | { |
| 567 | let _lock = weakref_lock::lock(obj as *const PyObject as usize); |
| 568 | if is_generic { |
| 569 | let generic_ptr = self.generic.load(Ordering::Relaxed); |
| 570 | if !generic_ptr.is_null() { |
| 571 | let generic = unsafe { &*generic_ptr }; |
| 572 | if generic.0.ref_count.safe_inc() { |
| 573 | return unsafe { PyRef::from_raw(generic_ptr) }; |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | // Allocate OUTSIDE the stripe lock. PyRef::new_ref may trigger |
| 580 | // maybe_collect → GC → WeakRefList::clear on another object that |
| 581 | // hashes to the same stripe, which would deadlock on the spinlock. |
| 582 | let weak_payload = PyWeak { |
| 583 | pointers: Pointers::new(), |
| 584 | wr_object: Radium::new(obj as *const PyObject as *mut PyObject), |
| 585 | callback: UnsafeCell::new(callback), |
| 586 | hash: Radium::new(crate::common::hash::SENTINEL), |
| 587 | }; |
| 588 | let weak = PyRef::new_ref(weak_payload, cls, dict); |
| 589 | |
| 590 | // Re-acquire lock for linked list insertion |
| 591 | let _lock = weakref_lock::lock(obj as *const PyObject as usize); |
| 592 | |
| 593 | // Re-check: another thread may have inserted a generic ref while we |
| 594 | // were allocating outside the lock. If so, reuse it and drop ours. |
| 595 | if is_generic { |
| 596 | let generic_ptr = self.generic.load(Ordering::Relaxed); |
| 597 | if !generic_ptr.is_null() { |
| 598 | let generic = unsafe { &*generic_ptr }; |
| 599 | if generic.0.ref_count.safe_inc() { |
| 600 | // Nullify wr_object so drop_inner won't unlink an |
| 601 | // un-inserted node (which would corrupt the list head). |
| 602 | weak.wr_object.store(ptr::null_mut(), Ordering::Relaxed); |
| 603 | return unsafe { PyRef::from_raw(generic_ptr) }; |
| 604 | } |
| 605 | } |
| 606 | } |
| 607 | |
| 608 | // Insert into linked list under stripe lock |
| 609 | let node_ptr = NonNull::from(&*weak); |
| 610 | unsafe { |
| 611 | let mut ptrs = WeakLink::pointers(node_ptr); |
| 612 | if is_generic { |
no test coverage detected