11use std:: {
22 any:: { Any , TypeId } ,
3+ mem:: MaybeUninit ,
34 sync:: {
45 Arc ,
5- atomic:: { AtomicBool , AtomicU32 , Ordering }
6- }
6+ atomic:: { AtomicU8 , AtomicU32 , AtomicU64 , Ordering }
7+ } ,
8+ time:: Duration
79} ;
810
911use parking_lot:: RwLock ;
1012
1113use crate :: {
1214 error:: MismatchedTypeError ,
1315 fetcher:: Fetcher ,
14- options:: Options ,
16+ options:: StoredOptions ,
1517 revalidate:: RevalidateIntent ,
1618 runtime:: Runtime ,
17- util:: { AtomicInstant , Instant , TaskSlot }
19+ util:: { AtomicBitwise , Instant , TaskSlot }
1820} ;
1921
22+ #[ repr( transparent) ]
23+ pub struct CacheEntryStatus ( AtomicU8 ) ;
24+
25+ impl CacheEntryStatus {
26+ pub const HAS_DATA : u8 = 1 << 0 ;
27+ pub const HAS_ERROR : u8 = 1 << 1 ;
28+
29+ pub const LOADING : u8 = 1 << 2 ;
30+ pub const VALIDATING : u8 = 1 << 3 ;
31+
32+ pub const ALIVE : u8 = 1 << 4 ;
33+ pub const USED_THIS_PASS : u8 = 1 << 5 ;
34+
35+ pub fn new ( ) -> Self {
36+ CacheEntryStatus ( AtomicU8 :: new ( 0 ) )
37+ }
38+
39+ pub fn load ( & self , ordering : Ordering ) -> u8 {
40+ self . 0 . load ( ordering)
41+ }
42+
43+ pub fn set ( & self , bits : u8 , ordering : Ordering ) -> bool {
44+ self . 0 . bits_set ( bits, ordering)
45+ }
46+
47+ pub fn get ( & self , bits : u8 , ordering : Ordering ) -> bool {
48+ self . 0 . bits_get ( bits, ordering)
49+ }
50+
51+ pub fn clear ( & self , bits : u8 , ordering : Ordering ) -> bool {
52+ self . 0 . bits_clear ( bits, ordering)
53+ }
54+ }
55+
2056pub struct CacheEntry < F : Fetcher , R : Runtime > {
21- pub key : F :: Key ,
22- data : Option < CacheEntryData > ,
23- pub error : Option < Arc < F :: Error > > ,
24- pub loading : bool ,
25- pub validating : bool ,
26-
27- pub revalidate_intent : RevalidateIntent ,
28- pub used_this_pass : AtomicBool ,
29- pub alive : AtomicBool ,
30- pub last_draw_time : AtomicInstant ,
31- pub last_request_time : Option < Instant > ,
57+ key : F :: Key ,
58+
59+ pub ( crate ) retry_count : AtomicU8 ,
60+ status : CacheEntryStatus ,
61+ revalidate_intent : RevalidateIntent ,
62+ data : MaybeUninit < CacheEntryData > ,
63+ error : MaybeUninit < Arc < F :: Error > > ,
64+
65+ base_time : Instant ,
66+ // offset from base time in nanos (up to ~584 years for 64 bits)
67+ last_draw_time_offset : AtomicU64 ,
68+ // offset from base time in nanos where u64::MAX is None, i.e. no request has been made
69+ last_request_time_offset : AtomicU64 ,
3270
3371 pub fetch_task : TaskSlot < R > ,
3472 pub refresh_task : TaskSlot < R > ,
35- pub ( crate ) retry_count : AtomicU32 ,
3673 pub retry_task : TaskSlot < R > ,
3774
38- pub ( crate ) persisted_instances : AtomicU32 ,
39- pub options : RwLock < Options < ( ) , F > >
75+ pub ( crate ) strong_count : AtomicU32 ,
76+ pub options : RwLock < StoredOptions >
4077}
4178
4279impl < F : Fetcher , R : Runtime > CacheEntry < F , R > {
4380 pub fn new ( runtime : R , key : F :: Key ) -> Self {
4481 Self {
4582 key,
46- data : None ,
47- error : None ,
48- loading : false ,
49- validating : false ,
5083
84+ retry_count : AtomicU8 :: new ( 0 ) ,
85+ status : CacheEntryStatus :: new ( ) ,
5186 revalidate_intent : RevalidateIntent :: default ( ) ,
52- used_this_pass : AtomicBool :: new ( false ) ,
53- alive : AtomicBool :: new ( false ) ,
54- last_draw_time : AtomicInstant :: now ( ) ,
55- last_request_time : None ,
87+ data : MaybeUninit :: uninit ( ) ,
88+ error : MaybeUninit :: uninit ( ) ,
89+
90+ base_time : Instant :: now ( ) ,
91+ last_draw_time_offset : AtomicU64 :: new ( 0 ) ,
92+ last_request_time_offset : AtomicU64 :: new ( u64:: MAX ) ,
5693
5794 fetch_task : TaskSlot :: new ( runtime. clone ( ) ) ,
5895 refresh_task : TaskSlot :: new ( runtime. clone ( ) ) ,
59- retry_count : AtomicU32 :: new ( 0 ) ,
6096 retry_task : TaskSlot :: new ( runtime) ,
6197
62- persisted_instances : AtomicU32 :: new ( 0 ) ,
63- options : RwLock :: new ( Options :: default ( ) )
98+ strong_count : AtomicU32 :: new ( 0 ) ,
99+ options : RwLock :: new ( StoredOptions :: default ( ) )
64100 }
65101 }
66102
67103 pub fn data < T : Send + Sync + ' static > ( & self ) -> Option < Result < Arc < F :: Response < T > > , MismatchedTypeError > > {
68- self . data . as_ref ( ) . map ( |data| match Arc :: downcast ( data. value . clone ( ) ) {
69- Ok ( x) => Ok ( x) ,
70- Err ( _) => Err ( MismatchedTypeError {
71- contained_type : self . data . type_id ( ) ,
72- wanted_type : TypeId :: of :: < T > ( ) ,
73-
74- #[ cfg( debug_assertions) ]
75- contained_type_name : data. type_name ,
76- #[ cfg( debug_assertions) ]
77- wanted_type_name : std:: any:: type_name :: < T > ( )
104+ if self . status . get ( CacheEntryStatus :: HAS_DATA , Ordering :: Acquire ) {
105+ let data = unsafe { self . data . assume_init_ref ( ) } ;
106+ Some ( match Arc :: downcast ( data. value . clone ( ) ) {
107+ Ok ( x) => Ok ( x) ,
108+ Err ( _) => Err ( MismatchedTypeError {
109+ contained_type : self . data . type_id ( ) ,
110+ wanted_type : TypeId :: of :: < T > ( ) ,
111+
112+ #[ cfg( debug_assertions) ]
113+ contained_type_name : data. type_name ,
114+ #[ cfg( debug_assertions) ]
115+ wanted_type_name : std:: any:: type_name :: < T > ( )
116+ } )
78117 } )
79- } )
118+ } else {
119+ None
120+ }
121+ }
122+
123+ pub fn error ( & self ) -> Option < & Arc < F :: Error > > {
124+ if self . status . get ( CacheEntryStatus :: HAS_ERROR , Ordering :: Acquire ) {
125+ let err = unsafe { self . error . assume_init_ref ( ) } ;
126+ Some ( err)
127+ } else {
128+ None
129+ }
80130 }
81131
82132 #[ inline]
83- pub fn has_data ( & self ) -> bool {
84- self . data . is_some ( )
133+ pub fn revalidate_intent ( & self ) -> & RevalidateIntent {
134+ & self . revalidate_intent
85135 }
86136
87- pub fn insert < T : Send + Sync + ' static > ( & mut self , data : Arc < F :: Response < T > > ) {
137+ #[ inline]
138+ pub fn status ( & self ) -> & CacheEntryStatus {
139+ & self . status
140+ }
141+
142+ #[ inline]
143+ pub fn key ( & self ) -> & F :: Key {
144+ & self . key
145+ }
146+
147+ pub fn insert < T : Send + Sync + ' static > ( & mut self , data : Arc < F :: Response < T > > ) -> Option < CacheEntryData > {
88148 self . insert_untyped (
89149 data as _ ,
90150 #[ cfg( debug_assertions) ]
91151 std:: any:: type_name :: < T > ( )
92- ) ;
152+ )
93153 }
94154
95- pub fn insert_untyped ( & mut self , data : Arc < dyn Any + Send + Sync > , #[ cfg( debug_assertions) ] type_name : & ' static str ) {
96- self . loading = false ;
97- self . validating = false ;
155+ pub fn insert_untyped ( & mut self , data : Arc < dyn Any + Send + Sync > , #[ cfg( debug_assertions) ] type_name : & ' static str ) -> Option < CacheEntryData > {
156+ self . status
157+ . clear ( CacheEntryStatus :: LOADING | CacheEntryStatus :: VALIDATING , Ordering :: Relaxed ) ; // we have mut
98158
99- self . data = Some ( CacheEntryData {
159+ let old_data = if self . status . set ( CacheEntryStatus :: HAS_DATA , Ordering :: Relaxed ) {
160+ Some ( unsafe { self . data . assume_init_read ( ) } )
161+ } else {
162+ None
163+ } ;
164+ self . data . write ( CacheEntryData {
100165 value : data,
101166 #[ cfg( debug_assertions) ]
102167 type_name
103168 } ) ;
104169
105- self . error . take ( ) ;
106- self . retry_count . store ( 0 , Ordering :: Release ) ;
170+ if self . status . clear ( CacheEntryStatus :: HAS_ERROR , Ordering :: Relaxed ) {
171+ unsafe { self . error . assume_init_drop ( ) } ;
172+ }
173+
174+ self . retry_count . store ( 0 , Ordering :: Relaxed ) ;
175+ self . last_request_time_offset
176+ . store ( instant_as_offset ( & self . base_time , Instant :: now ( ) ) , Ordering :: Relaxed ) ;
107177
108- self . last_request_time . replace ( Instant :: now ( ) ) ;
178+ old_data
109179 }
110180
111181 pub fn insert_error ( & mut self , error : Arc < F :: Error > ) {
112- self . loading = false ;
113- self . validating = false ;
182+ self . status
183+ . clear ( CacheEntryStatus :: LOADING | CacheEntryStatus :: VALIDATING , Ordering :: Relaxed ) ; // we have mut
114184
115- self . error = Some ( error) ;
116- self . last_request_time . replace ( Instant :: now ( ) ) ;
117- }
185+ if self . status . set ( CacheEntryStatus :: HAS_ERROR , Ordering :: Relaxed ) {
186+ unsafe { self . error . assume_init_drop ( ) } ;
187+ }
188+ self . error . write ( error) ;
118189
119- pub fn swap < T : Send + Sync + ' static > ( & mut self , data : Arc < F :: Response < T > > ) -> Option < CacheEntryData > {
120- let old_data = self . data . take ( ) ;
121- self . insert_untyped (
122- data,
123- #[ cfg( debug_assertions) ]
124- std:: any:: type_name :: < T > ( )
125- ) ;
126- old_data
190+ self . last_request_time_offset
191+ . store ( instant_as_offset ( & self . base_time , Instant :: now ( ) ) , Ordering :: Relaxed ) ;
127192 }
128193
129194 pub fn mark_used ( & self ) {
130- self . last_draw_time . store_now ( Ordering :: Release ) ;
131- self . used_this_pass . store ( true , Ordering :: Release ) ;
195+ self . last_draw_time_offset
196+ . store ( instant_as_offset ( & self . base_time , Instant :: now ( ) ) , Ordering :: Release ) ;
197+ self . status . set ( CacheEntryStatus :: USED_THIS_PASS , Ordering :: Release ) ;
198+ }
199+
200+ pub fn last_request_time ( & self , order : Ordering ) -> Option < Instant > {
201+ match self . last_request_time_offset . load ( order) {
202+ u64:: MAX => None ,
203+ offs => Some ( instant_from_offset ( & self . base_time , offs) )
204+ }
205+ }
206+
207+ pub fn last_draw_time ( & self , order : Ordering ) -> Instant {
208+ instant_from_offset ( & self . base_time , self . last_draw_time_offset . load ( order) )
132209 }
133210}
134211
@@ -137,3 +214,14 @@ pub struct CacheEntryData {
137214 #[ cfg( debug_assertions) ]
138215 pub type_name : & ' static str
139216}
217+
218+ fn instant_as_offset ( base : & Instant , new_value : Instant ) -> u64 {
219+ let offset = new_value - * base;
220+ offset. as_secs ( ) * 1_000_000_000 + u64:: from ( offset. subsec_nanos ( ) )
221+ }
222+
223+ fn instant_from_offset ( base : & Instant , offset_nanos : u64 ) -> Instant {
224+ let secs = offset_nanos / 1_000_000_000 ;
225+ let subsec_nanos = ( offset_nanos % 1_000_000_000 ) as u32 ;
226+ * base + Duration :: new ( secs, subsec_nanos)
227+ }
0 commit comments