@@ -44,6 +44,21 @@ impl Handle {
44
44
}
45
45
}
46
46
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
+
47
62
trait LocalData { }
48
63
impl < T : ' static > LocalData for T { }
49
64
@@ -77,7 +92,7 @@ impl<T: 'static> LocalData for T {}
77
92
//
78
93
// n.b. If TLS is used heavily in future, this could be made more efficient with
79
94
// a proper map.
80
- type TaskLocalMap = ~[ Option < ( * libc:: c_void , TLSValue , uint ) > ] ;
95
+ type TaskLocalMap = ~[ Option < ( * libc:: c_void , TLSValue , LoanState ) > ] ;
81
96
type TLSValue = ~LocalData : ;
82
97
83
98
fn cleanup_task_local_map ( map_ptr : * libc:: c_void ) {
@@ -152,9 +167,10 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
152
167
153
168
for map. mut_iter( ) . advance |entry| {
154
169
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( ) ) ;
158
174
}
159
175
// Move the data out of the `entry` slot via util::replace. This
160
176
// is guaranteed to succeed because we already matched on `Some`
@@ -192,6 +208,29 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
192
208
pub unsafe fn local_get < T : ' static , U > ( handle : Handle ,
193
209
key : local_data:: Key < T > ,
194
210
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 {
195
234
// This function must be extremely careful. Because TLS can store owned
196
235
// values, and we must have some form of `get` function other than `pop`,
197
236
// 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,
218
257
None => { return f ( None ) ; }
219
258
Some ( i) => {
220
259
let ret;
260
+ let mut return_loan = false ;
221
261
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
+ }
224
275
// data was created with `~~T as ~LocalData`, so we extract
225
276
// pointer part of the trait, (as ~~T), and then use
226
- // compiler coercions to achieve a '&' pointer
277
+ // compiler coercions to achieve a '&' pointer.
227
278
match * cast:: transmute :: < & TLSValue , & ( uint , ~~T ) > ( data) {
228
279
( _vtable, ref box) => {
229
280
let value: & T = * * box;
@@ -238,9 +289,11 @@ pub unsafe fn local_get<T: 'static, U>(handle: Handle,
238
289
// 'f' returned because `f` could have appended more TLS items which
239
290
// in turn relocated the vector. Hence we do another lookup here to
240
291
// 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
+ }
244
297
}
245
298
return ret;
246
299
}
@@ -269,9 +322,10 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
269
322
// First see if the map contains this key already
270
323
let curspot = map. iter ( ) . position ( |entry| {
271
324
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( ) )
275
329
}
276
330
true
277
331
}
@@ -286,7 +340,7 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
286
340
}
287
341
288
342
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 ) ) ) ; }
291
345
}
292
346
}
0 commit comments