@@ -44,6 +44,21 @@ impl Handle {
4444 }
4545}
4646
47+ #[ deriving( Eq ) ]
48+ enum LoanState {
49+ NoLoan , ImmLoan , MutLoan
50+ }
51+
52+ impl LoanState {
53+ fn describe ( & self ) -> & ' static str {
54+ match * self {
55+ NoLoan => "no loan" ,
56+ ImmLoan => "immutable" ,
57+ MutLoan => "mutable"
58+ }
59+ }
60+ }
61+
4762trait LocalData { }
4863impl < T : ' static > LocalData for T { }
4964
@@ -77,7 +92,7 @@ impl<T: 'static> LocalData for T {}
7792//
7893// n.b. If TLS is used heavily in future, this could be made more efficient with
7994// a proper map.
80- type TaskLocalMap = ~[ Option < ( * libc:: c_void , TLSValue , uint ) > ] ;
95+ type TaskLocalMap = ~[ Option < ( * libc:: c_void , TLSValue , LoanState ) > ] ;
8196type TLSValue = ~LocalData : ;
8297
8398fn cleanup_task_local_map ( map_ptr : * libc:: c_void ) {
@@ -152,9 +167,10 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
152167
153168 for map. mut_iter( ) . advance |entry| {
154169 match * entry {
155- Some ( ( k, _, loans) ) if k == key_value => {
156- if loans != 0 {
157- fail ! ( "TLS value has been loaned via get already" ) ;
170+ Some ( ( k, _, loan) ) if k == key_value => {
171+ if loan != NoLoan {
172+ fail!( "TLS value cannot be removed because it is already \
173+ borrowed as %s", loan. describe( ) ) ;
158174 }
159175 // Move the data out of the `entry` slot via util::replace. This
160176 // is guaranteed to succeed because we already matched on `Some`
@@ -192,6 +208,29 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
192208pub unsafe fn local_get < T : ' static , U > ( handle : Handle ,
193209 key : local_data:: Key < T > ,
194210 f : & fn ( Option < & T > ) -> U ) -> U {
211+ local_get_with ( handle, key, ImmLoan , f)
212+ }
213+
214+ pub unsafe fn local_get_mut < T : ' static , U > ( handle : Handle ,
215+ key : local_data:: Key < T > ,
216+ f : & fn ( Option < & mut T > ) -> U ) -> U {
217+ do local_get_with ( handle, key, MutLoan ) |x| {
218+ match x {
219+ None => f ( None ) ,
220+ // We're violating a lot of compiler guarantees with this
221+ // invocation of `transmute_mut`, but we're doing runtime checks to
222+ // ensure that it's always valid (only one at a time).
223+ //
224+ // there is no need to be upset!
225+ Some ( x) => { f ( Some ( cast:: transmute_mut ( x) ) ) }
226+ }
227+ }
228+ }
229+
230+ unsafe fn local_get_with < T : ' static , U > ( handle : Handle ,
231+ key : local_data:: Key < T > ,
232+ state : LoanState ,
233+ f : & fn ( Option < & T > ) -> U ) -> U {
195234 // This function must be extremely careful. Because TLS can store owned
196235 // values, and we must have some form of `get` function other than `pop`,
197236 // this function has to give a `&` reference back to the caller.
@@ -218,12 +257,24 @@ pub unsafe fn local_get<T: 'static, U>(handle: Handle,
218257 None => { return f ( None ) ; }
219258 Some ( i) => {
220259 let ret;
260+ let mut return_loan = false ;
221261 match map[ i] {
222- Some ( ( _, ref data, ref mut loans) ) => {
223- * loans = * loans + 1 ;
262+ Some ( ( _, ref data, ref mut loan) ) => {
263+ match ( state, * loan) {
264+ ( _, NoLoan ) => {
265+ * loan = state;
266+ return_loan = true ;
267+ }
268+ ( ImmLoan , ImmLoan ) => { }
269+ ( want, cur) => {
270+ fail ! ( "TLS slot cannot be borrowed as %s because \
271+ it is already borrowed as %s",
272+ want. describe( ) , cur. describe( ) ) ;
273+ }
274+ }
224275 // data was created with `~~T as ~LocalData`, so we extract
225276 // pointer part of the trait, (as ~~T), and then use
226- // compiler coercions to achieve a '&' pointer
277+ // compiler coercions to achieve a '&' pointer.
227278 match * cast:: transmute :: < & TLSValue , & ( uint , ~~T ) > ( data) {
228279 ( _vtable, ref box) => {
229280 let value: & T = * * box;
@@ -238,9 +289,11 @@ pub unsafe fn local_get<T: 'static, U>(handle: Handle,
238289 // 'f' returned because `f` could have appended more TLS items which
239290 // in turn relocated the vector. Hence we do another lookup here to
240291 // fixup the loans.
241- match map[ i] {
242- Some ( ( _, _, ref mut loans) ) => { * loans = * loans - 1 ; }
243- None => { libc:: abort ( ) ; }
292+ if return_loan {
293+ match map[ i] {
294+ Some ( ( _, _, ref mut loan) ) => { * loan = NoLoan ; }
295+ None => { libc:: abort ( ) ; }
296+ }
244297 }
245298 return ret;
246299 }
@@ -269,9 +322,10 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
269322 // First see if the map contains this key already
270323 let curspot = map. iter ( ) . position ( |entry| {
271324 match * entry {
272- Some ( ( ekey, _, loans) ) if key == ekey => {
273- if loans != 0 {
274- fail ! ( "TLS value has been loaned via get already" ) ;
325+ Some ( ( ekey, _, loan) ) if key == ekey => {
326+ if loan != NoLoan {
327+ fail ! ( "TLS value cannot be overwritten because it is
328+ already borrowed as %s" , loan. describe( ) )
275329 }
276330 true
277331 }
@@ -286,7 +340,7 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
286340 }
287341
288342 match insertion_position ( map, keyval) {
289- Some ( i) => { map[ i] = Some ( ( keyval, data, 0 ) ) ; }
290- None => { map. push ( Some ( ( keyval, data, 0 ) ) ) ; }
343+ Some ( i) => { map[ i] = Some ( ( keyval, data, NoLoan ) ) ; }
344+ None => { map. push ( Some ( ( keyval, data, NoLoan ) ) ) ; }
291345 }
292346}
0 commit comments