1+ use std:: any:: type_name;
12use std:: any:: Any ;
23use std:: borrow:: Borrow ;
34use std:: borrow:: BorrowMut ;
45use std:: convert:: identity;
56use std:: convert:: AsMut ;
67use std:: convert:: AsRef ;
8+ use std:: convert:: TryFrom ;
79use std:: marker:: PhantomData ;
810use std:: mem:: align_of;
911use std:: mem:: forget;
@@ -18,6 +20,9 @@ use std::ptr::null_mut;
1820use std:: ptr:: NonNull ;
1921use std:: rc:: Rc ;
2022use std:: sync:: Arc ;
23+ use std:: thread:: yield_now;
24+ use std:: time:: Duration ;
25+ use std:: time:: Instant ;
2126
2227// TODO use libc::intptr_t when stable.
2328// https://doc.rust-lang.org/1.7.0/libc/type.intptr_t.html
@@ -209,12 +214,20 @@ impl<T: Shared> Drop for SharedPtrBase<T> {
209214pub struct SharedPtr < T : Shared > ( SharedPtrBase < T > ) ;
210215
211216impl < T : Shared > SharedPtr < T > {
212- pub fn is_null ( & self ) -> bool {
213- <T as Shared >:: get ( & self . 0 ) . is_null ( )
217+ /// Asserts that the number of references to the shared inner value is equal
218+ /// to the `expected` count.
219+ ///
220+ /// This function relies on the C++ method `std::shared_ptr::use_count()`,
221+ /// which usually performs a relaxed load. This function will repeatedly call
222+ /// `use_count()` until it returns the expected value, for up to one second.
223+ /// Therefore it should probably not be used in performance critical code.
224+ #[ track_caller]
225+ pub fn assert_use_count_eq ( & self , expected : usize ) {
226+ assert_shared_ptr_use_count_eq ( "SharedPtr" , & self . 0 , expected) ;
214227 }
215228
216- pub fn use_count ( & self ) -> long {
217- <T as Shared >:: use_count ( & self . 0 )
229+ pub fn is_null ( & self ) -> bool {
230+ <T as Shared >:: get ( & self . 0 ) . is_null ( )
218231 }
219232
220233 pub fn take ( & mut self ) -> Option < SharedRef < T > > {
@@ -267,8 +280,16 @@ impl<T: Shared> From<SharedRef<T>> for SharedPtr<T> {
267280pub struct SharedRef < T : Shared > ( SharedPtrBase < T > ) ;
268281
269282impl < T : Shared > SharedRef < T > {
270- pub fn use_count ( & self ) -> long {
271- <T as Shared >:: use_count ( & self . 0 )
283+ /// Asserts that the number of references to the shared inner value is equal
284+ /// to the `expected` count.
285+ ///
286+ /// This function relies on the C++ method `std::shared_ptr::use_count()`,
287+ /// which usually performs a relaxed load. This function will repeatedly call
288+ /// `use_count()` until it returns the expected value, for up to one second.
289+ /// Therefore it should probably not be used in performance critical code.
290+ #[ track_caller]
291+ pub fn assert_use_count_eq ( & self , expected : usize ) {
292+ assert_shared_ptr_use_count_eq ( "SharedRef" , & self . 0 , expected) ;
272293 }
273294}
274295
@@ -303,6 +324,42 @@ impl<T: Shared> Borrow<T> for SharedRef<T> {
303324 }
304325}
305326
327+ #[ track_caller]
328+ fn assert_shared_ptr_use_count_eq < T : Shared > (
329+ wrapper_type_name : & str ,
330+ shared_ptr : & SharedPtrBase < T > ,
331+ expected : usize ,
332+ ) {
333+ let mut actual = T :: use_count ( shared_ptr) ;
334+ let ok = match long:: try_from ( expected) {
335+ Err ( _) => false , // Non-`long` value can never match actual use count.
336+ Ok ( expected) if actual == expected => true , // Fast path.
337+ Ok ( expected) => {
338+ pub const RETRY_TIMEOUT : Duration = Duration :: from_secs ( 1 ) ;
339+ let start = Instant :: now ( ) ;
340+ loop {
341+ yield_now ( ) ;
342+ actual = T :: use_count ( shared_ptr) ;
343+ if actual == expected {
344+ break true ;
345+ } else if start. elapsed ( ) > RETRY_TIMEOUT {
346+ break false ;
347+ }
348+ }
349+ }
350+ } ;
351+ assert ! (
352+ ok,
353+ "assertion failed: `{}<{}>` reference count does not match expectation\
354+ \n actual: {}\
355+ \n expected: {}",
356+ wrapper_type_name,
357+ type_name:: <T >( ) ,
358+ actual,
359+ expected
360+ ) ;
361+ }
362+
306363/// A trait for values with static lifetimes that are allocated at a fixed
307364/// address in memory. Practically speaking, that means they're either a
308365/// `&'static` reference, or they're heap-allocated in a `Arc`, `Box`, `Rc`,
@@ -672,38 +729,69 @@ mod tests {
672729 forget ( take ( p) ) ;
673730 }
674731
675- fn use_count ( _: & SharedPtrBase < Self > ) -> long {
676- unimplemented ! ( )
732+ fn use_count ( p : & SharedPtrBase < Self > ) -> long {
733+ match p {
734+ & Self :: SHARED_PTR_BASE_A => 1 ,
735+ & Self :: SHARED_PTR_BASE_B => 2 ,
736+ p if p == & Default :: default ( ) => 0 ,
737+ _ => unreachable ! ( ) ,
738+ }
677739 }
678740 }
679741
680742 #[ test]
681743 fn shared_ptr_and_shared_ref ( ) {
682744 let mut shared_ptr_a1 = SharedPtr ( MockSharedObj :: SHARED_PTR_BASE_A ) ;
683745 assert ! ( !shared_ptr_a1. is_null( ) ) ;
746+ shared_ptr_a1. assert_use_count_eq ( 1 ) ;
684747
685748 let shared_ref_a: SharedRef < _ > = shared_ptr_a1. take ( ) . unwrap ( ) ;
686749 assert_eq ! ( shared_ref_a. inner, 11111 ) ;
750+ shared_ref_a. assert_use_count_eq ( 1 ) ;
687751
688752 assert ! ( shared_ptr_a1. is_null( ) ) ;
753+ shared_ptr_a1. assert_use_count_eq ( 0 ) ;
689754
690755 let shared_ptr_a2: SharedPtr < _ > = shared_ref_a. into ( ) ;
691756 assert ! ( !shared_ptr_a2. is_null( ) ) ;
757+ shared_ptr_a2. assert_use_count_eq ( 1 ) ;
692758 assert_eq ! ( shared_ptr_a2. unwrap( ) . inner, 11111 ) ;
693759
694760 let mut shared_ptr_b1 = SharedPtr ( MockSharedObj :: SHARED_PTR_BASE_B ) ;
695761 assert ! ( !shared_ptr_b1. is_null( ) ) ;
762+ shared_ptr_b1. assert_use_count_eq ( 2 ) ;
696763
697764 let shared_ref_b: SharedRef < _ > = shared_ptr_b1. take ( ) . unwrap ( ) ;
698765 assert_eq ! ( shared_ref_b. inner, 22222 ) ;
766+ shared_ref_b. assert_use_count_eq ( 2 ) ;
699767
700768 assert ! ( shared_ptr_b1. is_null( ) ) ;
769+ shared_ptr_b1. assert_use_count_eq ( 0 ) ;
701770
702771 let shared_ptr_b2: SharedPtr < _ > = shared_ref_b. into ( ) ;
703772 assert ! ( !shared_ptr_b2. is_null( ) ) ;
773+ shared_ptr_b2. assert_use_count_eq ( 2 ) ;
704774 assert_eq ! ( shared_ptr_b2. unwrap( ) . inner, 22222 ) ;
705775 }
706776
777+ #[ test]
778+ #[ should_panic( expected = "assertion failed: \
779+ `SharedPtr<rusty_v8::support::tests::MockSharedObj>` reference count \
780+ does not match expectation") ]
781+ fn shared_ptr_use_count_assertion_failed ( ) {
782+ let shared_ptr: SharedPtr < MockSharedObj > = Default :: default ( ) ;
783+ shared_ptr. assert_use_count_eq ( 3 ) ;
784+ }
785+
786+ #[ test]
787+ #[ should_panic( expected = "assertion failed: \
788+ `SharedRef<rusty_v8::support::tests::MockSharedObj>` reference count \
789+ does not match expectation") ]
790+ fn shared_ref_use_count_assertion_failed ( ) {
791+ let shared_ref = SharedRef ( MockSharedObj :: SHARED_PTR_BASE_B ) ;
792+ shared_ref. assert_use_count_eq ( 7 ) ;
793+ }
794+
707795 static TEST_OBJ_DROPPED : AtomicBool = AtomicBool :: new ( false ) ;
708796
709797 struct TestObj {
0 commit comments