use super::heap_header::HeapHeader;
use std::sync::atomic::Ordering;
#[inline(always)]
pub unsafe fn v2_retain(ptr: *const HeapHeader) {
unsafe { (*ptr).refcount.fetch_add(1, Ordering::Relaxed) };
}
#[inline(always)]
pub unsafe fn v2_release(ptr: *const HeapHeader) -> bool {
let old = unsafe { (*ptr).refcount.fetch_sub(1, Ordering::Release) };
if old == 1 {
std::sync::atomic::fence(Ordering::Acquire);
true } else {
false
}
}
#[inline(always)]
pub unsafe fn v2_get_refcount(ptr: *const HeapHeader) -> u32 {
unsafe { (*ptr).refcount.load(Ordering::Relaxed) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::v2::heap_header::HEAP_KIND_V2_TYPED_ARRAY;
#[test]
fn test_v2_retain_increments() {
let h = HeapHeader::new(HEAP_KIND_V2_TYPED_ARRAY);
unsafe {
let ptr = &h as *const HeapHeader;
assert_eq!(v2_get_refcount(ptr), 1);
v2_retain(ptr);
assert_eq!(v2_get_refcount(ptr), 2);
v2_retain(ptr);
assert_eq!(v2_get_refcount(ptr), 3);
}
}
#[test]
fn test_v2_release_decrements() {
let h = HeapHeader::new(HEAP_KIND_V2_TYPED_ARRAY);
unsafe {
let ptr = &h as *const HeapHeader;
v2_retain(ptr); v2_retain(ptr);
assert!(!v2_release(ptr)); assert_eq!(v2_get_refcount(ptr), 2);
assert!(!v2_release(ptr)); assert_eq!(v2_get_refcount(ptr), 1);
assert!(v2_release(ptr)); }
}
#[test]
fn test_v2_release_returns_true_on_last_ref() {
let h = HeapHeader::new(HEAP_KIND_V2_TYPED_ARRAY);
unsafe {
let ptr = &h as *const HeapHeader;
assert!(v2_release(ptr)); }
}
#[test]
fn test_v2_retain_release_thread_safety() {
use std::sync::Arc;
let header = Arc::new(HeapHeader::new(HEAP_KIND_V2_TYPED_ARRAY));
let threads: Vec<_> = (0..8)
.map(|_| {
let h = Arc::clone(&header);
std::thread::spawn(move || {
let ptr = &*h as *const HeapHeader;
for _ in 0..1000 {
unsafe { v2_retain(ptr) };
}
for _ in 0..1000 {
unsafe { v2_release(ptr) };
}
})
})
.collect();
for t in threads {
t.join().unwrap();
}
unsafe {
let ptr = &*header as *const HeapHeader;
assert_eq!(v2_get_refcount(ptr), 1);
}
}
#[test]
fn test_v2_refcount_operations_match_header_methods() {
let h = HeapHeader::new(HEAP_KIND_V2_TYPED_ARRAY);
unsafe {
let ptr = &h as *const HeapHeader;
assert_eq!(v2_get_refcount(ptr), h.get_refcount());
v2_retain(ptr);
assert_eq!(v2_get_refcount(ptr), h.get_refcount());
assert_eq!(h.get_refcount(), 2);
let should_dealloc = v2_release(ptr);
assert!(!should_dealloc);
assert_eq!(v2_get_refcount(ptr), h.get_refcount());
assert_eq!(h.get_refcount(), 1);
}
}
}