Skip to content

Commit e3fb706

Browse files
committed
Work around stage0 to remove '@' requirements from TLS
1 parent cb5b9a4 commit e3fb706

File tree

5 files changed

+334
-75
lines changed

5 files changed

+334
-75
lines changed

src/libstd/local_data.rs

+40
Original file line numberDiff line numberDiff line change
@@ -66,34 +66,74 @@ pub type Key<'self,T> = &'self fn:Copy(v: T);
6666
* Remove a task-local data value from the table, returning the
6767
* reference that was originally created to insert it.
6868
*/
69+
#[cfg(stage0)]
70+
pub unsafe fn pop<T: 'static>(key: Key<@T>) -> Option<@T> {
71+
local_pop(Handle::new(), key)
72+
}
73+
/**
74+
* Remove a task-local data value from the table, returning the
75+
* reference that was originally created to insert it.
76+
*/
77+
#[cfg(not(stage0))]
6978
pub unsafe fn pop<T: 'static>(key: Key<T>) -> Option<T> {
7079
local_pop(Handle::new(), key)
7180
}
7281
/**
7382
* Retrieve a task-local data value. It will also be kept alive in the
7483
* table until explicitly removed.
7584
*/
85+
#[cfg(stage0)]
86+
pub unsafe fn get<T: 'static>(key: Key<@T>) -> Option<@T> {
87+
local_get(Handle::new(), key, |loc| loc.map(|&x| *x))
88+
}
89+
/**
90+
* Retrieve a task-local data value. It will also be kept alive in the
91+
* table until explicitly removed.
92+
*/
93+
#[cfg(not(stage0))]
7694
pub unsafe fn get<T: 'static>(key: Key<@T>) -> Option<@T> {
7795
local_get(Handle::new(), key, |loc| loc.map(|&x| *x))
7896
}
7997
/**
8098
* Store a value in task-local data. If this key already has a value,
8199
* that value is overwritten (and its destructor is run).
82100
*/
101+
#[cfg(stage0)]
83102
pub unsafe fn set<T: 'static>(key: Key<@T>, data: @T) {
84103
local_set(Handle::new(), key, data)
85104
}
105+
/**
106+
* Store a value in task-local data. If this key already has a value,
107+
* that value is overwritten (and its destructor is run).
108+
*/
109+
#[cfg(not(stage0))]
110+
pub unsafe fn set<T: 'static>(key: Key<T>, data: T) {
111+
local_set(Handle::new(), key, data)
112+
}
86113
/**
87114
* Modify a task-local data value. If the function returns 'None', the
88115
* data is removed (and its reference dropped).
89116
*/
117+
#[cfg(stage0)]
90118
pub unsafe fn modify<T: 'static>(key: Key<@T>,
91119
f: &fn(Option<@T>) -> Option<@T>) {
92120
match f(pop(key)) {
93121
Some(next) => { set(key, next); }
94122
None => {}
95123
}
96124
}
125+
/**
126+
* Modify a task-local data value. If the function returns 'None', the
127+
* data is removed (and its reference dropped).
128+
*/
129+
#[cfg(not(stage0))]
130+
pub unsafe fn modify<T: 'static>(key: Key<T>,
131+
f: &fn(Option<T>) -> Option<T>) {
132+
match f(pop(key)) {
133+
Some(next) => { set(key, next); }
134+
None => {}
135+
}
136+
}
97137

98138
#[test]
99139
fn test_tls_multitask() {

src/libstd/rt/task.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,15 @@ mod test {
167167

168168
#[test]
169169
fn tls() {
170-
use local_data::*;
170+
use local_data;
171171
do run_in_newsched_task() {
172172
unsafe {
173173
fn key(_x: @~str) { }
174-
local_data_set(key, @~"data");
175-
assert!(*local_data_get(key).get() == ~"data");
174+
local_data::set(key, @~"data");
175+
assert!(*local_data::get(key).get() == ~"data");
176176
fn key2(_x: @~str) { }
177-
local_data_set(key2, @~"data");
178-
assert!(*local_data_get(key2).get() == ~"data");
177+
local_data::set(key2, @~"data");
178+
assert!(*local_data::get(key2).get() == ~"data");
179179
}
180180
}
181181
}

src/libstd/task/local_data_priv.rs

+56-70
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@
1313
use cast;
1414
use libc;
1515
use local_data;
16-
use managed::raw::BoxRepr;
1716
use prelude::*;
1817
use ptr;
1918
use sys;
2019
use task::rt;
21-
use unstable::intrinsics;
2220
use util;
2321

2422
use super::rt::rust_task;
@@ -50,15 +48,15 @@ impl Handle {
5048
trait LocalData {}
5149
impl<T: 'static> LocalData for T {}
5250

53-
// The task-local-map actuall stores all TLS information. Right now it's a list
51+
// The task-local-map actually stores all TLS information. Right now it's a list
5452
// of triples of (key, value, loans). The key is a code pointer (right now at
5553
// least), the value is a trait so destruction can work, and the loans value
5654
// is a count of the number of times the value is currently on loan via
5755
// `local_data_get`.
5856
//
5957
// TLS is designed to be able to store owned data, so `local_data_get` must
6058
// return a borrowed pointer to this data. In order to have a proper lifetime, a
61-
// borrowed pointer is insted yielded to a closure specified to the `get`
59+
// borrowed pointer is instead yielded to a closure specified to the `get`
6260
// function. As a result, it would be unsound to perform `local_data_set` on the
6361
// same key inside of a `local_data_get`, so we ensure at runtime that this does
6462
// not happen.
@@ -68,7 +66,7 @@ impl<T: 'static> LocalData for T {}
6866
// n.b. If TLS is used heavily in future, this could be made more efficient with
6967
// a proper map.
7068
type TaskLocalMap = ~[Option<(*libc::c_void, TLSValue, uint)>];
71-
type TLSValue = @LocalData;
69+
type TLSValue = ~LocalData:;
7270

7371
fn cleanup_task_local_map(map_ptr: *libc::c_void) {
7472
unsafe {
@@ -136,28 +134,8 @@ unsafe fn key_to_key_value<T: 'static>(key: local_data::Key<T>) -> *libc::c_void
136134
return pair.code as *libc::c_void;
137135
}
138136

139-
unsafe fn transmute_back<'a, T>(data: &'a TLSValue) -> (*BoxRepr, &'a T) {
140-
// Currently, a TLSValue is an '@Trait' instance which means that its actual
141-
// representation is a pair of (vtable, box). Also, because of issue #7673
142-
// the box actually points to another box which has the data. Hence, to get
143-
// a pointer to the actual value that we're interested in, we decode the
144-
// trait pointer and pass through one layer of boxes to get to the actual
145-
// data we're interested in.
146-
//
147-
// The reference count of the containing @Trait box is already taken care of
148-
// because the TLSValue is owned by the containing TLS map which means that
149-
// the reference count is at least one. Extra protections are then added at
150-
// runtime to ensure that once a loan on a value in TLS has been given out,
151-
// the value isn't modified by another user.
152-
let (_vt, box) = *cast::transmute::<&TLSValue, &(uint, *BoxRepr)>(data);
153-
154-
return (box, cast::transmute(&(*box).data));
155-
}
156-
157137
pub unsafe fn local_pop<T: 'static>(handle: Handle,
158138
key: local_data::Key<T>) -> Option<T> {
159-
// If you've never seen horrendously unsafe code written in rust before,
160-
// just feel free to look a bit farther...
161139
let map = get_local_map(handle);
162140
let key_value = key_to_key_value(key);
163141

@@ -175,25 +153,23 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
175153
None => libc::abort(),
176154
};
177155

178-
// First, via some various cheats/hacks, we extract the value
179-
// contained within the TLS box. This leaves a big chunk of
180-
// memory which needs to be deallocated now.
181-
let (chunk, inside) = transmute_back(&data);
182-
let inside = cast::transmute_mut(inside);
183-
let ret = ptr::read_ptr(inside);
156+
// Move `data` into transmute to get out the memory that it
157+
// owns, we must free it manually later.
158+
let (_vtable, box): (uint, ~~T) = cast::transmute(data);
159+
160+
// Read the box's value (using the compiler's built-in
161+
// auto-deref functionality to obtain a pointer to the base)
162+
let ret = ptr::read_ptr(cast::transmute::<&T, *mut T>(*box));
184163

185-
// Forget the trait box because we're about to manually
186-
// deallocate the other box. And for my next trick (kids don't
187-
// try this at home), transmute the chunk of @ memory from the
188-
// @-trait box to a pointer to a zero-sized '@' block which will
189-
// then cause it to get properly deallocated, but it won't touch
190-
// any of the uninitialized memory beyond the end.
191-
cast::forget(data);
192-
let chunk: *mut BoxRepr = cast::transmute(chunk);
193-
(*chunk).header.type_desc =
194-
cast::transmute(intrinsics::get_tydesc::<()>());
195-
let _: @() = cast::transmute(chunk);
164+
// Finally free the allocated memory. we don't want this to
165+
// actually touch the memory inside because it's all duplicated
166+
// now, so the box is transmuted to a 0-sized type. We also use
167+
// a type which references `T` because currently the layout
168+
// could depend on whether T contains managed pointers or not.
169+
let _: ~~[T, ..0] = cast::transmute(box);
196170

171+
// Everything is now deallocated, and we own the value that was
172+
// located inside TLS, so we now return it.
197173
return Some(ret);
198174
}
199175
_ => {}
@@ -213,9 +189,17 @@ pub unsafe fn local_get<T: 'static, U>(handle: Handle,
213189
for map.mut_iter().advance |entry| {
214190
match *entry {
215191
Some((k, ref data, ref mut loans)) if k == key_value => {
192+
let ret;
216193
*loans = *loans + 1;
217-
let (_, val) = transmute_back(data);
218-
let ret = f(Some(val));
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));
201+
}
202+
}
219203
*loans = *loans - 1;
220204
return ret;
221205
}
@@ -225,44 +209,46 @@ pub unsafe fn local_get<T: 'static, U>(handle: Handle,
225209
return f(None);
226210
}
227211

228-
// FIXME(#7673): This shouldn't require '@', it should use '~'
229212
pub unsafe fn local_set<T: 'static>(handle: Handle,
230-
key: local_data::Key<@T>,
231-
data: @T) {
213+
key: local_data::Key<T>,
214+
data: T) {
232215
let map = get_local_map(handle);
233216
let keyval = key_to_key_value(key);
234217

235218
// When the task-local map is destroyed, all the data needs to be cleaned
236-
// up. For this reason we can't do some clever tricks to store '@T' as a
219+
// up. For this reason we can't do some clever tricks to store '~T' as a
237220
// '*c_void' or something like that. To solve the problem, we cast
238221
// everything to a trait (LocalData) which is then stored inside the map.
239222
// Upon destruction of the map, all the objects will be destroyed and the
240223
// traits have enough information about them to destroy themselves.
241-
let data = @data as @LocalData;
242-
243-
// First, try to insert it if we already have it.
244-
for map.mut_iter().advance |entry| {
245-
match *entry {
246-
Some((key, ref mut value, loans)) if key == keyval => {
247-
if loans != 0 {
248-
fail!("TLS value has been loaned via get already");
224+
//
225+
// FIXME(#7673): This should be "~data as ~LocalData" (without the colon at
226+
// the end, and only one sigil)
227+
let data = ~~data as ~LocalData:;
228+
229+
fn insertion_position(map: &mut TaskLocalMap,
230+
key: *libc::c_void) -> Option<uint> {
231+
// First see if the map contains this key already
232+
let curspot = map.iter().position(|entry| {
233+
match *entry {
234+
Some((ekey, _, loans)) if key == ekey => {
235+
if loans != 0 {
236+
fail!("TLS value has been loaned via get already");
237+
}
238+
true
249239
}
250-
util::replace(value, data);
251-
return;
240+
_ => false,
252241
}
253-
_ => {}
242+
});
243+
// If it doesn't contain the key, just find a slot that's None
244+
match curspot {
245+
Some(i) => Some(i),
246+
None => map.iter().position(|entry| entry.is_none())
254247
}
255248
}
256-
// Next, search for an open spot
257-
for map.mut_iter().advance |entry| {
258-
match *entry {
259-
Some(*) => {}
260-
None => {
261-
*entry = Some((keyval, data, 0));
262-
return;
263-
}
264-
}
249+
250+
match insertion_position(map, keyval) {
251+
Some(i) => { map[i] = Some((keyval, data, 0)); }
252+
None => { map.push(Some((keyval, data, 0))); }
265253
}
266-
// Finally push it on the end of the list
267-
map.push(Some((keyval, data, 0)));
268254
}

0 commit comments

Comments
 (0)