@@ -48,23 +48,36 @@ impl Handle {
4848trait LocalData { }
4949impl < T : ' static > LocalData for T { }
5050
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 .
5656//
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.
6362//
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 *.
6578//
6679// n.b. If TLS is used heavily in future, this could be made more efficient with
67- // a proper map.
80+ // a proper map.
6881type TaskLocalMap = ~[ Option < ( * libc:: c_void , TLSValue , uint ) > ] ;
6982type TLSValue = ~LocalData : ;
7083
@@ -181,32 +194,59 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
181194pub unsafe fn local_get < T : ' static , U > ( handle : Handle ,
182195 key : local_data:: Key < T > ,
183196 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.
187211 let map = get_local_map ( handle) ;
188212 let key_value = key_to_key_value ( key) ;
189- for map. mut_iter( ) . advance |entry| {
213+
214+ let pos = map. iter ( ) . position ( |entry| {
190215 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+ }
201234 }
202235 }
203- * loans = * loans - 1 ;
204- return ret;
236+ _ => libc:: abort ( )
205237 }
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;
207248 }
208249 }
209- return f ( None ) ;
210250}
211251
212252pub unsafe fn local_set < T : ' static > ( handle : Handle ,
0 commit comments