Skip to content

Commit 948a624

Browse files
committed
Add a get_mut method for TLS
Simulates borrow checks for '@mut' boxes, or at least it's the same idea.
1 parent 9db1903 commit 948a624

File tree

2 files changed

+145
-16
lines changed

2 files changed

+145
-16
lines changed

src/libstd/local_data.rs

+76-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ magic.
4141

4242
use prelude::*;
4343

44-
use task::local_data_priv::{local_get, local_pop, local_set, Handle};
44+
use task::local_data_priv::*;
4545

4646
#[cfg(test)] use task;
4747

@@ -95,6 +95,13 @@ pub fn get<T: 'static, U>(key: Key<@T>, f: &fn(Option<&@T>) -> U) -> U {
9595
pub fn get<T: 'static, U>(key: Key<T>, f: &fn(Option<&T>) -> U) -> U {
9696
unsafe { local_get(Handle::new(), key, f) }
9797
}
98+
/**
99+
* Retrieve a mutable borrowed pointer to a task-local data value.
100+
*/
101+
#[cfg(not(stage0))]
102+
pub fn get_mut<T: 'static, U>(key: Key<T>, f: &fn(Option<&mut T>) -> U) -> U {
103+
unsafe { local_get_mut(Handle::new(), key, f) }
104+
}
98105
/**
99106
* Store a value in task-local data. If this key already has a value,
100107
* that value is overwritten (and its destructor is run).
@@ -262,6 +269,34 @@ fn test_static_pointer() {
262269
fn test_owned() {
263270
static key: Key<~int> = &Key;
264271
set(key, ~1);
272+
273+
do get(key) |v| {
274+
do get(key) |v| {
275+
do get(key) |v| {
276+
assert_eq!(**v.unwrap(), 1);
277+
}
278+
assert_eq!(**v.unwrap(), 1);
279+
}
280+
assert_eq!(**v.unwrap(), 1);
281+
}
282+
set(key, ~2);
283+
do get(key) |v| {
284+
assert_eq!(**v.unwrap(), 2);
285+
}
286+
}
287+
288+
#[test]
289+
fn test_get_mut() {
290+
static key: Key<int> = &Key;
291+
set(key, 1);
292+
293+
do get_mut(key) |v| {
294+
*v.unwrap() = 2;
295+
}
296+
297+
do get(key) |v| {
298+
assert_eq!(*v.unwrap(), 2);
299+
}
265300
}
266301

267302
#[test]
@@ -283,3 +318,43 @@ fn test_same_key_type() {
283318
get(key4, |x| assert_eq!(*x.unwrap(), 4));
284319
get(key5, |x| assert_eq!(*x.unwrap(), 5));
285320
}
321+
322+
#[test]
323+
#[should_fail]
324+
fn test_nested_get_set1() {
325+
static key: Key<int> = &Key;
326+
set(key, 4);
327+
do get(key) |_| {
328+
set(key, 4);
329+
}
330+
}
331+
332+
#[test]
333+
#[should_fail]
334+
fn test_nested_get_mut2() {
335+
static key: Key<int> = &Key;
336+
set(key, 4);
337+
do get(key) |_| {
338+
get_mut(key, |_| {})
339+
}
340+
}
341+
342+
#[test]
343+
#[should_fail]
344+
fn test_nested_get_mut3() {
345+
static key: Key<int> = &Key;
346+
set(key, 4);
347+
do get_mut(key) |_| {
348+
get(key, |_| {})
349+
}
350+
}
351+
352+
#[test]
353+
#[should_fail]
354+
fn test_nested_get_mut4() {
355+
static key: Key<int> = &Key;
356+
set(key, 4);
357+
do get_mut(key) |_| {
358+
get_mut(key, |_| {})
359+
}
360+
}

src/libstd/task/local_data_priv.rs

+69-15
Original file line numberDiff line numberDiff line change
@@ -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+
4762
trait LocalData {}
4863
impl<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)>];
8196
type TLSValue = ~LocalData:;
8297

8398
fn 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,
192208
pub 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

Comments
 (0)