1
+ use crossbeam:: atomic:: AtomicCell ;
1
2
use std:: fmt;
2
3
use std:: hash:: Hash ;
3
4
use std:: marker:: PhantomData ;
5
+ use std:: ptr:: NonNull ;
4
6
7
+ use crate :: alloc:: Alloc ;
5
8
use crate :: durability:: Durability ;
6
9
use crate :: id:: AsId ;
7
10
use crate :: ingredient:: fmt_index;
@@ -24,16 +27,24 @@ pub trait Configuration: Sized + 'static {
24
27
/// The end user struct
25
28
type Struct < ' db > : Copy ;
26
29
27
- /// Create an end-user struct from the salsa id
30
+ /// Create an end-user struct from the underlying raw pointer.
28
31
///
29
32
/// This call is an "end-step" to the tracked struct lookup/creation
30
33
/// process in a given revision: it occurs only when the struct is newly
31
34
/// created or, if a struct is being reused, after we have updated its
32
35
/// fields (or confirmed it is green and no updates are required).
33
- fn struct_from_id < ' db > ( id : Id ) -> Self :: Struct < ' db > ;
34
-
35
- /// Deref the struct to yield the underlying id.
36
- fn deref_struct ( s : Self :: Struct < ' _ > ) -> Id ;
36
+ ///
37
+ /// # Safety
38
+ ///
39
+ /// Requires that `ptr` represents a "confirmed" value in this revision,
40
+ /// which means that it will remain valid and immutable for the remainder of this
41
+ /// revision, represented by the lifetime `'db`.
42
+ unsafe fn struct_from_raw < ' db > ( ptr : NonNull < Value < Self > > ) -> Self :: Struct < ' db > ;
43
+
44
+ /// Deref the struct to yield the underlying value struct.
45
+ /// Since we are still part of the `'db` lifetime in which the struct was created,
46
+ /// this deref is safe, and the value-struct fields are immutable and verified.
47
+ fn deref_struct ( s : Self :: Struct < ' _ > ) -> & Value < Self > ;
37
48
}
38
49
39
50
pub trait InternedData : Sized + Eq + Hash + Clone { }
@@ -55,6 +66,14 @@ pub struct IngredientImpl<C: Configuration> {
55
66
/// Deadlock requirement: We access `value_map` while holding lock on `key_map`, but not vice versa.
56
67
key_map : FxDashMap < C :: Data < ' static > , Id > ,
57
68
69
+ /// Maps from an interned id to its data.
70
+ ///
71
+ /// Deadlock requirement: We access `value_map` while holding lock on `key_map`, but not vice versa.
72
+ value_map : FxDashMap < Id , Alloc < Value < C > > > ,
73
+
74
+ /// counter for the next id.
75
+ counter : AtomicCell < u32 > ,
76
+
58
77
/// Stores the revision when this interned ingredient was last cleared.
59
78
/// You can clear an interned table at any point, deleting all its entries,
60
79
/// but that will make anything dependent on those entries dirty and in need
93
112
Self {
94
113
ingredient_index,
95
114
key_map : Default :: default ( ) ,
115
+ value_map : Default :: default ( ) ,
116
+ counter : AtomicCell :: default ( ) ,
96
117
reset_at : Revision :: start ( ) ,
97
118
}
98
119
}
@@ -101,10 +122,6 @@ where
101
122
unsafe { std:: mem:: transmute ( data) }
102
123
}
103
124
104
- unsafe fn from_internal_data < ' db > ( & ' db self , data : & C :: Data < ' static > ) -> & C :: Data < ' db > {
105
- unsafe { std:: mem:: transmute ( data) }
106
- }
107
-
108
125
pub fn intern_id < ' db > (
109
126
& ' db self ,
110
127
db : & ' db dyn crate :: Database ,
@@ -132,46 +149,66 @@ where
132
149
if let Some ( guard) = self . key_map . get ( & internal_data) {
133
150
let id = * guard;
134
151
drop ( guard) ;
135
- return C :: struct_from_id ( id) ;
152
+ return self . interned_value ( id) ;
136
153
}
137
154
138
155
match self . key_map . entry ( internal_data. clone ( ) ) {
139
156
// Data has been interned by a racing call, use that ID instead
140
157
dashmap:: mapref:: entry:: Entry :: Occupied ( entry) => {
141
158
let id = * entry. get ( ) ;
142
159
drop ( entry) ;
143
- C :: struct_from_id ( id)
160
+ self . interned_value ( id)
144
161
}
145
162
146
163
// We won any races so should intern the data
147
164
dashmap:: mapref:: entry:: Entry :: Vacant ( entry) => {
148
- let zalsa = db. zalsa ( ) ;
149
- let table = zalsa. table ( ) ;
150
- let next_id = zalsa_local. allocate ( table, self . ingredient_index , internal_data) ;
165
+ let next_id = self . counter . fetch_add ( 1 ) ;
166
+ let next_id = crate :: id:: Id :: from_u32 ( next_id) ;
167
+ let value = self . value_map . entry ( next_id) . or_insert ( Alloc :: new ( Value {
168
+ id : next_id,
169
+ fields : internal_data,
170
+ } ) ) ;
171
+ let value_raw = value. as_raw ( ) ;
172
+ drop ( value) ;
151
173
entry. insert ( next_id) ;
152
- C :: struct_from_id ( next_id)
174
+ // SAFETY: Items are only removed from the `value_map` with an `&mut self` reference.
175
+ unsafe { C :: struct_from_raw ( value_raw) }
153
176
}
154
177
}
155
178
}
156
179
180
+ pub fn interned_value ( & self , id : Id ) -> C :: Struct < ' _ > {
181
+ let r = self . value_map . get ( & id) . unwrap ( ) ;
182
+
183
+ // SAFETY: Items are only removed from the `value_map` with an `&mut self` reference.
184
+ unsafe { C :: struct_from_raw ( r. as_raw ( ) ) }
185
+ }
186
+
157
187
/// Lookup the data for an interned value based on its id.
158
188
/// Rarely used since end-users generally carry a struct with a pointer directly
159
189
/// to the interned item.
160
- pub fn data < ' db > ( & ' db self , db : & ' db dyn Database , id : Id ) -> & ' db C :: Data < ' db > {
161
- let internal_data = db. zalsa ( ) . table ( ) . get :: < C :: Data < ' static > > ( id) ;
162
- unsafe { self . from_internal_data ( internal_data) }
190
+ pub fn data ( & self , id : Id ) -> & C :: Data < ' _ > {
191
+ C :: deref_struct ( self . interned_value ( id) ) . data ( )
163
192
}
164
193
165
194
/// Lookup the fields from an interned struct.
166
195
/// Note that this is not "leaking" since no dependency edge is required.
167
- pub fn fields < ' db > ( & ' db self , db : & ' db dyn Database , s : C :: Struct < ' db > ) -> & ' db C :: Data < ' db > {
168
- self . data ( db, C :: deref_struct ( s) )
196
+ pub fn fields < ' db > ( & ' db self , s : C :: Struct < ' db > ) -> & ' db C :: Data < ' db > {
197
+ C :: deref_struct ( s) . data ( )
198
+ }
199
+
200
+ /// Variant of `data` that takes a (unnecessary) database argument.
201
+ /// This exists because tracked functions sometimes use true interning and sometimes use
202
+ /// [`IdentityInterner`][], which requires the database argument.
203
+ pub fn data_with_db < ' db , DB : ?Sized > ( & ' db self , id : Id , _db : & ' db DB ) -> & ' db C :: Data < ' db > {
204
+ self . data ( id)
169
205
}
170
206
171
207
pub fn reset ( & mut self , revision : Revision ) {
172
208
assert ! ( revision > self . reset_at) ;
173
209
self . reset_at = revision;
174
210
self . key_map . clear ( ) ;
211
+ self . value_map . clear ( ) ;
175
212
}
176
213
}
177
214
0 commit comments