13
13
use cast;
14
14
use libc;
15
15
use local_data:: LocalDataKey ;
16
+ use managed:: raw:: BoxRepr ;
16
17
use prelude:: * ;
18
+ use ptr;
17
19
use sys;
18
20
use task:: rt;
21
+ use unstable:: intrinsics;
22
+ use util;
19
23
20
24
use super :: rt:: rust_task;
21
25
use rt:: task:: { Task , LocalStorage } ;
@@ -47,15 +51,24 @@ trait LocalData {}
47
51
impl < T : ' static > LocalData for T { }
48
52
49
53
// The task-local-map actuall stores all TLS information. Right now it's a list
50
- // of key-value pairs. Each value is an actual Rust type so that when the map is
51
- // destroyed all of the contents are destroyed. Each of the keys are actually
52
- // addresses which don't need to be destroyed.
54
+ // of triples of (key, value, loans). The key is a code pointer (right now at
55
+ // least), the value is a trait so destruction can work, and the loans value
56
+ // is a count of the number of times the value is currently on loan via
57
+ // `local_data_get`.
58
+ //
59
+ // TLS is designed to be able to store owned data, so `local_data_get` must
60
+ // return a borrowed pointer to this data. In order to have a proper lifetime, a
61
+ // borrowed pointer is insted yielded to a closure specified to the `get`
62
+ // function. As a result, it would be unsound to perform `local_data_set` on the
63
+ // same key inside of a `local_data_get`, so we ensure at runtime that this does
64
+ // not happen.
53
65
//
54
66
// n.b. Has to be a pointer at outermost layer; the foreign call returns void *.
55
67
//
56
68
// n.b. If TLS is used heavily in future, this could be made more efficient with
57
69
// a proper map.
58
- type TaskLocalMap = ~[ Option < ( * libc:: c_void , @LocalData ) > ] ;
70
+ type TaskLocalMap = ~[ Option < ( * libc:: c_void , TLSValue , uint ) > ] ;
71
+ type TLSValue = @LocalData ;
59
72
60
73
fn cleanup_task_local_map ( map_ptr : * libc:: c_void ) {
61
74
unsafe {
@@ -123,64 +136,98 @@ unsafe fn key_to_key_value<T: 'static>(key: LocalDataKey<T>) -> *libc::c_void {
123
136
return pair. code as * libc:: c_void ;
124
137
}
125
138
126
- // If returning Some(..), returns with @T with the map's reference. Careful!
127
- unsafe fn local_data_lookup < T : ' static > ( map : & TaskLocalMap ,
128
- key : LocalDataKey < T > )
129
- -> Option < ( uint , @T ) >
130
- {
131
- use managed:: raw:: BoxRepr ;
139
+ unsafe fn transmute_back < ' a , T > ( data : & ' a TLSValue ) -> ( * BoxRepr , & ' a T ) {
140
+ // Currently, a TLSValue is an '@Trait' instance which means that its actual
141
+ // representation is a pair of (vtable, box). Also, because of issue #7673
142
+ // the box actually points to another box which has the data. Hence, to get
143
+ // a pointer to the actual value that we're interested in, we decode the
144
+ // trait pointer and pass through one layer of boxes to get to the actual
145
+ // data we're interested in.
146
+ //
147
+ // The reference count of the containing @Trait box is already taken care of
148
+ // because the TLSValue is owned by the containing TLS map which means that
149
+ // the reference count is at least one. Extra protections are then added at
150
+ // runtime to ensure that once a loan on a value in TLS has been given out,
151
+ // the value isn't modified by another user.
152
+ let ( _vt, box) = * cast:: transmute :: < & TLSValue , & ( uint , * BoxRepr ) > ( data) ;
153
+
154
+ return ( box, cast:: transmute ( & ( * box) . data ) ) ;
155
+ }
132
156
157
+ pub unsafe fn local_pop < T : ' static > ( handle : Handle ,
158
+ key : LocalDataKey < T > ) -> Option < T > {
159
+ // If you've never seen horrendously unsafe code written in rust before,
160
+ // just feel free to look a bit farther...
161
+ let map = get_local_map ( handle) ;
133
162
let key_value = key_to_key_value ( key) ;
134
- for map. iter( ) . enumerate( ) . advance |( i, entry) | {
163
+
164
+ for map. mut_iter( ) . advance |entry| {
135
165
match * entry {
136
- Some ( ( k, ref data) ) if k == key_value => {
137
- // We now have the correct 'data' as type @LocalData which we
138
- // need to somehow transmute this back to @T. This was
139
- // originally stored into the map as:
140
- //
141
- // let data = @T;
142
- // let element = @data as @LocalData;
143
- // insert(key, element);
144
- //
145
- // This means that the element stored is a 2-word pair (because
146
- // it's a trait). The second element is the vtable (we don't
147
- // need it), and the first element is actually '@@T'. Not only
148
- // is this @@T, but it's a pointer to the base of the @@T (box
149
- // and all), so we have to traverse this to find the actual
150
- // pointer that we want.
151
- let ( _vtable, box) =
152
- * cast:: transmute :: < & @LocalData , & ( uint , * BoxRepr ) > ( data) ;
153
- let ptr: & @T = cast:: transmute ( & ( * box) . data ) ;
154
- return Some ( ( i, * ptr) ) ;
166
+ Some ( ( k, _, loans) ) if k == key_value => {
167
+ if loans != 0 {
168
+ fail ! ( "TLS value has been loaned via get already" ) ;
169
+ }
170
+ // Move the data out of the `entry` slot via util::replace. This
171
+ // is guaranteed to succeed because we already matched on `Some`
172
+ // above.
173
+ let data = match util:: replace ( entry, None ) {
174
+ Some ( ( _, data, _) ) => data,
175
+ None => libc:: abort ( ) ,
176
+ } ;
177
+
178
+ // First, via some various cheats/hacks, we extract the value
179
+ // contained within the TLS box. This leaves a big chunk of
180
+ // memory which needs to be deallocated now.
181
+ let ( chunk, inside) = transmute_back ( & data) ;
182
+ let inside = cast:: transmute_mut ( inside) ;
183
+ let ret = ptr:: read_ptr ( inside) ;
184
+
185
+ // Forget the trait box because we're about to manually
186
+ // deallocate the other box. And for my next trick (kids don't
187
+ // try this at home), transmute the chunk of @ memory from the
188
+ // @-trait box to a pointer to a zero-sized '@' block which will
189
+ // then cause it to get properly deallocated, but it won't touch
190
+ // any of the uninitialized memory beyond the end.
191
+ cast:: forget ( data) ;
192
+ let chunk: * mut BoxRepr = cast:: transmute ( chunk) ;
193
+ ( * chunk) . header . type_desc =
194
+ cast:: transmute ( intrinsics:: get_tydesc :: < ( ) > ( ) ) ;
195
+ let _: @( ) = cast:: transmute ( chunk) ;
196
+
197
+ return Some ( ret) ;
155
198
}
156
199
_ => { }
157
200
}
158
201
}
159
202
return None ;
160
203
}
161
204
162
- pub unsafe fn local_pop < T : ' static > ( handle : Handle ,
163
- key : LocalDataKey < T > ) -> Option < @T > {
205
+ pub unsafe fn local_get< T : ' static , U > ( handle : Handle ,
206
+ key : LocalDataKey < T > ,
207
+ f : & fn ( Option < & T > ) -> U ) -> U {
208
+ // This does in theory take multiple mutable loans on the tls map, but the
209
+ // references returned are never removed because the map is only increasing
210
+ // in size (it never shrinks).
164
211
let map = get_local_map ( handle) ;
165
- match local_data_lookup ( map, key) {
166
- Some ( ( index, data) ) => {
167
- map[ index] = None ;
168
- Some ( data)
212
+ let key_value = key_to_key_value ( key) ;
213
+ for map. mut_iter( ) . advance |entry| {
214
+ match * entry {
215
+ Some ( ( k, ref data, ref mut loans) ) if k == key_value => {
216
+ * loans = * loans + 1 ;
217
+ let ( _, val) = transmute_back( data) ;
218
+ let ret = f( Some ( val) ) ;
219
+ * loans = * loans - 1 ;
220
+ return ret;
221
+ }
222
+ _ => { }
169
223
}
170
- None => None
171
- }
172
- }
173
-
174
- pub unsafe fn local_get < T : ' static > ( handle : Handle ,
175
- key : LocalDataKey < T > ) -> Option < @T > {
176
- match local_data_lookup ( get_local_map ( handle) , key) {
177
- Some ( ( _, data) ) => Some ( data) ,
178
- None => None
179
224
}
225
+ return f( None ) ;
180
226
}
181
227
228
+ // FIXME(#7673): This shouldn't require '@', it should use '~'
182
229
pub unsafe fn local_set < T : ' static > ( handle: Handle ,
183
- key : LocalDataKey < T > ,
230
+ key: LocalDataKey < @ T > ,
184
231
data: @T ) {
185
232
let map = get_local_map ( handle) ;
186
233
let keyval = key_to_key_value ( key) ;
@@ -191,16 +238,31 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
191
238
// everything to a trait (LocalData) which is then stored inside the map.
192
239
// Upon destruction of the map, all the objects will be destroyed and the
193
240
// traits have enough information about them to destroy themselves.
194
- let entry = Some ( ( keyval, @data as @LocalData ) ) ;
195
-
196
- match local_data_lookup ( map, key) {
197
- Some ( ( index, _) ) => { map[ index] = entry; }
198
- None => {
199
- // Find an empty slot. If not, grow the vector.
200
- match map. iter ( ) . position ( |x| x. is_none ( ) ) {
201
- Some ( empty_index) => { map[ empty_index] = entry; }
202
- None => { map. push ( entry) ; }
241
+ let data = @data as @LocalData ;
242
+
243
+ // First, try to insert it if we already have it.
244
+ for map. mut_iter( ) . advance |entry| {
245
+ match * entry {
246
+ Some ( ( key, ref mut value, loans) ) if key == keyval => {
247
+ if loans != 0 {
248
+ fail ! ( "TLS value has been loaned via get already" ) ;
249
+ }
250
+ util:: replace( value, data) ;
251
+ return ;
252
+ }
253
+ _ => { }
254
+ }
255
+ }
256
+ // Next, search for an open spot
257
+ for map. mut_iter( ) . advance |entry| {
258
+ match * entry {
259
+ Some( * ) => { }
260
+ None => {
261
+ * entry = Some ( ( keyval, data, 0 ) ) ;
262
+ return ;
203
263
}
204
264
}
205
265
}
266
+ // Finally push it on the end of the list
267
+ map. push( Some ( ( keyval, data, 0 ) ) ) ;
206
268
}
0 commit comments