@@ -48,23 +48,36 @@ impl Handle {
48
48
trait LocalData { }
49
49
impl < T : ' static > LocalData for T { }
50
50
51
- // The task-local-map actually stores all TLS information. Right now it's a list
52
- // of triples of (key, value, loans). The key is a code pointer (right now at
53
- // least), the value is a trait so destruction can work, and the loans value
54
- // is a count of the number of times the value is currently on loan via
55
- // `local_data_get` .
51
+ // The task-local-map stores all TLS information for the currently running task.
52
+ // It is stored as an owned pointer into the runtime, and it's only allocated
53
+ // when TLS is used for the first time. This map must be very carefully
54
+ // constructed because it has many mutable loans unsoundly handed out on it to
55
+ // the various invocations of TLS requests .
56
56
//
57
- // TLS is designed to be able to store owned data, so `local_data_get` must
58
- // return a borrowed pointer to this data. In order to have a proper lifetime, a
59
- // borrowed pointer is instead yielded to a closure specified to the `get`
60
- // function. As a result, it would be unsound to perform `local_data_set` on the
61
- // same key inside of a `local_data_get`, so we ensure at runtime that this does
62
- // not happen.
57
+ // One of the most important operations is loaning a value via `get` to a
58
+ // caller. In doing so, the slot that the TLS entry is occupying cannot be
59
+ // invalidated because upon returning it's loan state must be updated. Currently
60
+ // the TLS map is a vector, but this is possibly dangerous because the vector
61
+ // can be reallocated/moved when new values are pushed onto it.
63
62
//
64
- // n.b. Has to be a pointer at outermost layer; the foreign call returns void *.
63
+ // This problem currently isn't solved in a very elegant way. Inside the `get`
64
+ // function, it internally "invalidates" all references after the loan is
65
+ // finished and looks up into the vector again. In theory this will prevent
66
+ // pointers from being moved under our feet so long as LLVM doesn't go too crazy
67
+ // with the optimizations.
68
+ //
69
+ // n.b. Other structures are not sufficient right now:
70
+ // * HashMap uses ~[T] internally (push reallocates/moves)
71
+ // * TreeMap is plausible, but it's in extra
72
+ // * dlist plausible, but not in std
73
+ // * a custom owned linked list was attempted, but difficult to write
74
+ // and involved a lot of extra code bloat
75
+ //
76
+ // n.b. Has to be stored with a pointer at outermost layer; the foreign call
77
+ // returns void *.
65
78
//
66
79
// n.b. If TLS is used heavily in future, this could be made more efficient with
67
- // a proper map.
80
+ // a proper map.
68
81
type TaskLocalMap = ~[ Option < ( * libc:: c_void , TLSValue , uint ) > ] ;
69
82
type TLSValue = ~LocalData : ;
70
83
@@ -181,32 +194,59 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
181
194
pub unsafe fn local_get < T : ' static , U > ( handle : Handle ,
182
195
key : local_data:: Key < T > ,
183
196
f : & fn ( Option < & T > ) -> U ) -> U {
184
- // This does in theory take multiple mutable loans on the tls map, but the
185
- // references returned are never removed because the map is only increasing
186
- // in size (it never shrinks).
197
+ // This function must be extremely careful. Because TLS can store owned
198
+ // values, and we must have some form of `get` function other than `pop`,
199
+ // this function has to give a `&` reference back to the caller.
200
+ //
201
+ // One option is to return the reference, but this cannot be sound because
202
+ // the actual lifetime of the object is not known. The slot in TLS could not
203
+ // be modified until the object goes out of scope, but the TLS code cannot
204
+ // know when this happens.
205
+ //
206
+ // For this reason, the reference is yielded to a specified closure. This
207
+ // way the TLS code knows exactly what the lifetime of the yielded pointer
208
+ // is, allowing callers to acquire references to owned data. This is also
209
+ // sound so long as measures are taken to ensure that while a TLS slot is
210
+ // loaned out to a caller, it's not modified recursively.
187
211
let map = get_local_map ( handle) ;
188
212
let key_value = key_to_key_value ( key) ;
189
- for map. mut_iter( ) . advance |entry| {
213
+
214
+ let pos = map. iter ( ) . position ( |entry| {
190
215
match * entry {
191
- Some ( ( k, ref data, ref mut loans) ) if k == key_value => {
192
- let ret;
193
- * loans = * loans + 1 ;
194
- // data was created with `~~T as ~LocalData`, so we extract
195
- // pointer part of the trait, (as ~~T), and then use compiler
196
- // coercions to achieve a '&' pointer
197
- match * cast:: transmute :: < & TLSValue , & ( uint , ~~T ) > ( data) {
198
- ( _vtable, ref box) => {
199
- let value: & T = * * box;
200
- ret = f ( Some ( value) ) ;
216
+ Some ( ( k, _, _) ) if k == key_value => true , _ => false
217
+ }
218
+ } ) ;
219
+ match pos {
220
+ None => { return f ( None ) ; }
221
+ Some ( i) => {
222
+ let ret;
223
+ match map[ i] {
224
+ Some ( ( _, ref data, ref mut loans) ) => {
225
+ * loans = * loans + 1 ;
226
+ // data was created with `~~T as ~LocalData`, so we extract
227
+ // pointer part of the trait, (as ~~T), and then use
228
+ // compiler coercions to achieve a '&' pointer
229
+ match * cast:: transmute :: < & TLSValue , & ( uint , ~~T ) > ( data) {
230
+ ( _vtable, ref box) => {
231
+ let value: & T = * * box;
232
+ ret = f ( Some ( value) ) ;
233
+ }
201
234
}
202
235
}
203
- * loans = * loans - 1 ;
204
- return ret;
236
+ _ => libc:: abort ( )
205
237
}
206
- _ => { }
238
+
239
+ // n.b. 'data' and 'loans' are both invalid pointers at the point
240
+ // 'f' returned because `f` could have appended more TLS items which
241
+ // in turn relocated the vector. Hence we do another lookup here to
242
+ // fixup the loans.
243
+ match map[ i] {
244
+ Some ( ( _, _, ref mut loans) ) => { * loans = * loans - 1 ; }
245
+ None => { libc:: abort ( ) ; }
246
+ }
247
+ return ret;
207
248
}
208
249
}
209
- return f ( None ) ;
210
250
}
211
251
212
252
pub unsafe fn local_set < T : ' static > ( handle : Handle ,
0 commit comments