22//@ needs-threads
33
44use std:: alloc:: { GlobalAlloc , Layout , System } ;
5- use std:: thread:: Thread ;
6- use std:: sync:: atomic:: { AtomicUsize , AtomicBool , Ordering } ;
5+ use std:: hint:: black_box;
6+ use std:: sync:: atomic:: { AtomicBool , AtomicUsize , Ordering } ;
7+ use std:: thread:: { Thread , ThreadId } ;
78
89static GLOBAL : AtomicUsize = AtomicUsize :: new ( 0 ) ;
10+ static SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS : AtomicBool = AtomicBool :: new ( false ) ;
11+ static LOCAL_TRY_WITH_SUCCEEDED_ALLOC : AtomicBool = AtomicBool :: new ( false ) ;
12+ static LOCAL_TRY_WITH_SUCCEEDED_DEALLOC : AtomicBool = AtomicBool :: new ( false ) ;
913
10- struct Local ( Thread ) ;
14+ struct LocalForAllocatorWithoutDrop ( ThreadId ) ;
1115
12- thread_local ! {
13- static LOCAL : Local = {
14- GLOBAL . fetch_or( 1 , Ordering :: Relaxed ) ;
15- Local ( std:: thread:: current( ) )
16- } ;
17- }
16+ struct LocalForAllocatorWithDrop ( Thread ) ;
1817
19- impl Drop for Local {
18+ impl Drop for LocalForAllocatorWithDrop {
2019 fn drop ( & mut self ) {
2120 GLOBAL . fetch_or ( 2 , Ordering :: Relaxed ) ;
2221 }
2322}
2423
25- static SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS : AtomicBool = AtomicBool :: new ( false ) ;
24+ struct LocalForUser ( u32 ) ;
25+
26+ impl Drop for LocalForUser {
27+ // A user might call the global allocator in a thread-local drop.
28+ fn drop ( & mut self ) {
29+ self . 0 += 1 ;
30+ drop ( black_box ( Box :: new ( self . 0 ) ) )
31+ }
32+ }
33+
34+ thread_local ! {
35+ static LOCAL_FOR_USER0 : LocalForUser = LocalForUser ( 0 ) ;
36+ static LOCAL_FOR_ALLOCATOR_WITHOUT_DROP : LocalForAllocatorWithoutDrop = {
37+ LocalForAllocatorWithoutDrop ( std:: thread:: current( ) . id( ) )
38+ } ;
39+ static LOCAL_FOR_ALLOCATOR_WITH_DROP : LocalForAllocatorWithDrop = {
40+ GLOBAL . fetch_or( 1 , Ordering :: Relaxed ) ;
41+ LocalForAllocatorWithDrop ( std:: thread:: current( ) )
42+ } ;
43+ static LOCAL_FOR_USER1 : LocalForUser = LocalForUser ( 1 ) ;
44+ }
2645
2746#[ global_allocator]
2847static ALLOC : Alloc = Alloc ;
@@ -33,9 +52,19 @@ unsafe impl GlobalAlloc for Alloc {
3352 // Make sure we aren't re-entrant.
3453 assert ! ( !SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS . load( Ordering :: Relaxed ) ) ;
3554 SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS . store ( true , Ordering :: Relaxed ) ;
36- LOCAL . with ( |local| {
55+
56+ // Should be infallible.
57+ LOCAL_FOR_ALLOCATOR_WITHOUT_DROP . with ( |local| {
58+ assert ! ( local. 0 == std:: thread:: current( ) . id( ) ) ;
59+ } ) ;
60+
61+ // May fail once thread-local destructors start running, and ours has
62+ // been ran.
63+ let try_with_ret = LOCAL_FOR_ALLOCATOR_WITH_DROP . try_with ( |local| {
3764 assert ! ( local. 0 . id( ) == std:: thread:: current( ) . id( ) ) ;
3865 } ) ;
66+ LOCAL_TRY_WITH_SUCCEEDED_ALLOC . fetch_or ( try_with_ret. is_ok ( ) , Ordering :: Relaxed ) ;
67+
3968 let ret = unsafe { System . alloc ( layout) } ;
4069 SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS . store ( false , Ordering :: Relaxed ) ;
4170 ret
@@ -45,20 +74,34 @@ unsafe impl GlobalAlloc for Alloc {
4574 // Make sure we aren't re-entrant.
4675 assert ! ( !SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS . load( Ordering :: Relaxed ) ) ;
4776 SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS . store ( true , Ordering :: Relaxed ) ;
48- LOCAL . with ( |local| {
77+
78+ // Should be infallible.
79+ LOCAL_FOR_ALLOCATOR_WITHOUT_DROP . with ( |local| {
80+ assert ! ( local. 0 == std:: thread:: current( ) . id( ) ) ;
81+ } ) ;
82+
83+ // May fail once thread-local destructors start running, and ours has
84+ // been ran.
85+ let try_with_ret = LOCAL_FOR_ALLOCATOR_WITH_DROP . try_with ( |local| {
4986 assert ! ( local. 0 . id( ) == std:: thread:: current( ) . id( ) ) ;
5087 } ) ;
88+ LOCAL_TRY_WITH_SUCCEEDED_DEALLOC . fetch_or ( try_with_ret. is_ok ( ) , Ordering :: Relaxed ) ;
89+
5190 unsafe { System . dealloc ( ptr, layout) }
5291 SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS . store ( false , Ordering :: Relaxed ) ;
5392 }
5493}
5594
5695fn main ( ) {
5796 std:: thread:: spawn ( || {
97+ LOCAL_FOR_USER0 . with ( |l| assert ! ( l. 0 == 0 ) ) ;
5898 std:: hint:: black_box ( vec ! [ 1 , 2 ] ) ;
5999 assert ! ( GLOBAL . load( Ordering :: Relaxed ) == 1 ) ;
100+ LOCAL_FOR_USER1 . with ( |l| assert ! ( l. 0 == 1 ) ) ;
60101 } )
61102 . join ( )
62103 . unwrap ( ) ;
63104 assert ! ( GLOBAL . load( Ordering :: Relaxed ) == 3 ) ;
105+ assert ! ( LOCAL_TRY_WITH_SUCCEEDED_ALLOC . load( Ordering :: Relaxed ) ) ;
106+ assert ! ( LOCAL_TRY_WITH_SUCCEEDED_DEALLOC . load( Ordering :: Relaxed ) ) ;
64107}
0 commit comments