@@ -16,7 +16,10 @@ struct RafInner {
1616 callback : RafCallback ,
1717 use_deprecated : bool ,
1818 is_prepared : bool ,
19- data : Option < * mut c_void > ,
19+ // Incremented on every start(). Pending frame callbacks belonging to an
20+ // older generation drop themselves on their next fire instead of
21+ // re-posting, so a stop()/start() pair can never spawn duplicate chains.
22+ generation : u64 ,
2023 thread_id : Option < std:: thread:: ThreadId > ,
2124}
2225
@@ -26,57 +29,63 @@ pub struct Raf(
2629 Arc < parking_lot:: Condvar > ,
2730) ;
2831
32+ // Payload handed to AChoreographer_postFrameCallback. A posted frame callback
33+ // cannot be cancelled, so the callback itself is the SOLE owner of this
34+ // allocation: on each fire it either re-posts the same box for the next frame
35+ // or drops it. No other code path may free it — freeing it from stop() or
36+ // clear_callback() while a callback is still queued is a guaranteed
37+ // use-after-free at the next vsync.
38+ struct FrameChain {
39+ raf : Raf ,
40+ generation : u64 ,
41+ }
42+
2943impl Raf {
3044 extern "C" fn callback ( frame_time_nanos : c_long , data : * mut std:: os:: raw:: c_void ) {
3145 if data. is_null ( ) {
3246 return ;
3347 }
3448
35- let data_ptr = data ;
36- let raf_ptr = data as * mut Raf ;
37- let raf_ref = unsafe { & * raf_ptr } ;
49+ // Take ownership for this invocation; re-posted below if still live.
50+ let chain = unsafe { Box :: from_raw ( data as * mut FrameChain ) } ;
51+ let raf = & chain . raf ;
3852
3953 {
40- let mut lock = raf_ref. 0 . lock ( ) ;
41- if !lock. started {
42- let stored = lock. data . take ( ) ;
43- drop ( lock) ;
44- if let Some ( p) = stored {
45- let _ = unsafe { Box :: from_raw ( p as * mut Raf ) } ;
46- }
54+ let lock = raf. 0 . lock ( ) ;
55+ if !lock. started || lock. generation != chain. generation {
56+ // Chain is dead (stopped, or superseded by a newer start()).
57+ // Dropping `chain` releases the allocation.
4758 return ;
4859 }
49- raf_ref . 1 . fetch_add ( 1 , Ordering :: SeqCst ) ;
60+ raf . 1 . fetch_add ( 1 , Ordering :: SeqCst ) ;
5061 }
5162
5263 {
53- let lock = raf_ref . 0 . lock ( ) ;
64+ let lock = raf . 0 . lock ( ) ;
5465 if let Some ( callback) = lock. callback . as_ref ( ) {
5566 callback ( frame_time_nanos. into ( ) ) ;
5667 }
5768 }
5869
59- raf_ref. 1 . fetch_sub ( 1 , Ordering :: SeqCst ) ;
70+ raf. 1 . fetch_sub ( 1 , Ordering :: SeqCst ) ;
71+ raf. 2 . notify_all ( ) ;
6072
61- raf_ref. 2 . notify_all ( ) ;
73+ let repost = {
74+ let lock = raf. 0 . lock ( ) ;
75+ lock. started && lock. generation == chain. generation && lock. use_deprecated
76+ } ;
6277
63- unsafe {
64- let instance = AChoreographer_getInstance ( ) ;
65- let mut lock = raf_ref. 0 . lock ( ) ;
66- if lock. started {
67- if lock. use_deprecated {
68- AChoreographer_postFrameCallback ( instance, Some ( Raf :: callback) , data_ptr) ;
69- } else {
70- // post 64 variant if available
71- }
72- } else {
73- let stored = lock. data . take ( ) ;
74- drop ( lock) ;
75- if let Some ( p) = stored {
76- let _ = Box :: from_raw ( p as * mut Raf ) ;
77- }
78+ if repost {
79+ unsafe {
80+ let instance = AChoreographer_getInstance ( ) ;
81+ AChoreographer_postFrameCallback (
82+ instance,
83+ Some ( Raf :: callback) ,
84+ Box :: into_raw ( chain) as * mut c_void ,
85+ ) ;
7886 }
7987 }
88+ // else: `chain` drops here, ending this frame chain.
8089 }
8190
8291 pub fn new ( callback : RafCallback ) -> Self {
@@ -86,7 +95,7 @@ impl Raf {
8695 callback,
8796 is_prepared : false ,
8897 use_deprecated : true , //*crate::API_LEVEL.get().unwrap_or(&-1) < 24,
89- data : None ,
98+ generation : 0 ,
9099 thread_id : None ,
91100 } ) ) ,
92101 Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
@@ -95,27 +104,42 @@ impl Raf {
95104 }
96105
97106 pub fn start ( & self ) {
98- let mut lock = self . 0 . lock ( ) ;
99- if !lock. is_prepared {
100- unsafe {
101- ndk:: looper:: ThreadLooper :: prepare ( ) ;
107+ let generation;
108+ {
109+ let mut lock = self . 0 . lock ( ) ;
110+ if lock. started {
111+ return ;
112+ }
113+ if !lock. is_prepared {
114+ unsafe {
115+ ndk:: looper:: ThreadLooper :: prepare ( ) ;
116+ }
117+ lock. is_prepared = true ;
118+ }
119+
120+ lock. thread_id = Some ( std:: thread:: current ( ) . id ( ) ) ;
121+ lock. generation += 1 ;
122+ lock. started = true ;
123+ generation = lock. generation ;
124+ if !lock. use_deprecated {
125+ // #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
126+ // AChoreographer_postFrameCallback64(...)
127+ return ;
102128 }
103- lock. is_prepared = true ;
104129 }
105130
106- lock. thread_id = Some ( std:: thread:: current ( ) . id ( ) ) ;
131+ let chain = Box :: new ( FrameChain {
132+ raf : self . clone ( ) ,
133+ generation,
134+ } ) ;
107135 unsafe {
108136 let instance = AChoreographer_getInstance ( ) ;
109- let data = Box :: into_raw ( Box :: new ( self . clone ( ) ) ) as * mut c_void ;
110- lock. data = Some ( data) ;
111- if lock. use_deprecated {
112- AChoreographer_postFrameCallback ( instance, Some ( Raf :: callback) , data) ;
113- } else {
114- // #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
115- // AChoreographer_postFrameCallback64(instance, Some(Raf::callback), data.cast());
116- }
137+ AChoreographer_postFrameCallback (
138+ instance,
139+ Some ( Raf :: callback) ,
140+ Box :: into_raw ( chain) as * mut c_void ,
141+ ) ;
117142 }
118- lock. started = true ;
119143 }
120144
121145 pub fn stop ( & self ) {
@@ -129,8 +153,8 @@ impl Raf {
129153 let lock = self . 0 . lock ( ) ;
130154 if let Some ( tid) = lock. thread_id {
131155 if tid == std:: thread:: current ( ) . id ( ) {
132- drop ( lock ) ;
133- self . clear_callback ( ) ;
156+ // Same thread as the choreographer callback: a callback
157+ // cannot be mid-flight right now, nothing to wait for.
134158 return true ;
135159 }
136160 }
@@ -151,14 +175,11 @@ impl Raf {
151175 }
152176
153177 pub fn clear_callback ( & self ) {
178+ // Only detach the JS-side callback. The FrameChain allocation stays
179+ // alive until the already-queued choreographer callback fires, sees
180+ // started == false (or a stale generation), and drops it itself.
154181 let mut lock = self . 0 . lock ( ) ;
155182 lock. callback = None ;
156- if self . 1 . load ( Ordering :: SeqCst ) == 0 {
157- if let Some ( p) = lock. data . take ( ) {
158- drop ( lock) ;
159- let _ = unsafe { Box :: from_raw ( p as * mut Raf ) } ;
160- }
161- }
162183 }
163184
164185 pub fn set_callback ( & self , callback : RafCallback ) {
0 commit comments