Skip to content

Commit 5c3a2e7

Browse files
committed
Change TLS to almost be able to contain owned types
1 parent 692a22e commit 5c3a2e7

File tree

4 files changed

+145
-87
lines changed

4 files changed

+145
-87
lines changed

src/libstd/condition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub struct Handler<T, U> {
2626

2727
pub struct Condition<'self, T, U> {
2828
name: &'static str,
29-
key: local_data::LocalDataKey<'self, Handler<T, U>>
29+
key: local_data::LocalDataKey<'self, @Handler<T, U>>
3030
}
3131

3232
impl<'self, T, U> Condition<'self, T, U> {

src/libstd/local_data.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,41 +46,35 @@ use task::local_data_priv::{local_get, local_pop, local_set, Handle};
4646
*
4747
* These two cases aside, the interface is safe.
4848
*/
49-
pub type LocalDataKey<'self,T> = &'self fn:Copy(v: @T);
49+
pub type LocalDataKey<'self,T> = &'self fn:Copy(v: T);
5050

5151
/**
5252
* Remove a task-local data value from the table, returning the
5353
* reference that was originally created to insert it.
5454
*/
55-
pub unsafe fn local_data_pop<T: 'static>(
56-
key: LocalDataKey<T>) -> Option<@T> {
57-
55+
pub unsafe fn local_data_pop<T: 'static>(key: LocalDataKey<T>) -> Option<T> {
5856
local_pop(Handle::new(), key)
5957
}
6058
/**
6159
* Retrieve a task-local data value. It will also be kept alive in the
6260
* table until explicitly removed.
6361
*/
64-
pub unsafe fn local_data_get<T: 'static>(
65-
key: LocalDataKey<T>) -> Option<@T> {
66-
67-
local_get(Handle::new(), key)
62+
pub unsafe fn local_data_get<T: 'static>(key: LocalDataKey<@T>) -> Option<@T> {
63+
local_get(Handle::new(), key, |loc| loc.map(|&x| *x))
6864
}
6965
/**
7066
* Store a value in task-local data. If this key already has a value,
7167
* that value is overwritten (and its destructor is run).
7268
*/
73-
pub unsafe fn local_data_set<T: 'static>(
74-
key: LocalDataKey<T>, data: @T) {
75-
69+
pub unsafe fn local_data_set<T: 'static>(key: LocalDataKey<@T>, data: @T) {
7670
local_set(Handle::new(), key, data)
7771
}
7872
/**
7973
* Modify a task-local data value. If the function returns 'None', the
8074
* data is removed (and its reference dropped).
8175
*/
8276
pub unsafe fn local_data_modify<T: 'static>(
83-
key: LocalDataKey<T>,
77+
key: LocalDataKey<@T>,
8478
modify_fn: &fn(Option<@T>) -> Option<@T>) {
8579

8680
let cur = local_data_pop(key);

src/libstd/task/local_data_priv.rs

Lines changed: 117 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
use cast;
1414
use libc;
1515
use local_data::LocalDataKey;
16+
use managed::raw::BoxRepr;
1617
use prelude::*;
18+
use ptr;
1719
use sys;
1820
use task::rt;
21+
use unstable::intrinsics;
22+
use util;
1923

2024
use super::rt::rust_task;
2125
use rt::task::{Task, LocalStorage};
@@ -47,15 +51,24 @@ trait LocalData {}
4751
impl<T: 'static> LocalData for T {}
4852

4953
// The task-local-map actuall stores all TLS information. Right now it's a list
50-
// of key-value pairs. Each value is an actual Rust type so that when the map is
51-
// destroyed all of the contents are destroyed. Each of the keys are actually
52-
// addresses which don't need to be destroyed.
54+
// of triples of (key, value, loans). The key is a code pointer (right now at
55+
// least), the value is a trait so destruction can work, and the loans value
56+
// is a count of the number of times the value is currently on loan via
57+
// `local_data_get`.
58+
//
59+
// TLS is designed to be able to store owned data, so `local_data_get` must
60+
// 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`
62+
// function. As a result, it would be unsound to perform `local_data_set` on the
63+
// same key inside of a `local_data_get`, so we ensure at runtime that this does
64+
// not happen.
5365
//
5466
// n.b. Has to be a pointer at outermost layer; the foreign call returns void *.
5567
//
5668
// n.b. If TLS is used heavily in future, this could be made more efficient with
5769
// a proper map.
58-
type TaskLocalMap = ~[Option<(*libc::c_void, @LocalData)>];
70+
type TaskLocalMap = ~[Option<(*libc::c_void, TLSValue, uint)>];
71+
type TLSValue = @LocalData;
5972

6073
fn cleanup_task_local_map(map_ptr: *libc::c_void) {
6174
unsafe {
@@ -123,64 +136,98 @@ unsafe fn key_to_key_value<T: 'static>(key: LocalDataKey<T>) -> *libc::c_void {
123136
return pair.code as *libc::c_void;
124137
}
125138

126-
// If returning Some(..), returns with @T with the map's reference. Careful!
127-
unsafe fn local_data_lookup<T: 'static>(map: &TaskLocalMap,
128-
key: LocalDataKey<T>)
129-
-> Option<(uint, @T)>
130-
{
131-
use managed::raw::BoxRepr;
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+
}
132156

157+
pub unsafe fn local_pop<T: 'static>(handle: Handle,
158+
key: LocalDataKey<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...
161+
let map = get_local_map(handle);
133162
let key_value = key_to_key_value(key);
134-
for map.iter().enumerate().advance |(i, entry)| {
163+
164+
for map.mut_iter().advance |entry| {
135165
match *entry {
136-
Some((k, ref data)) if k == key_value => {
137-
// We now have the correct 'data' as type @LocalData which we
138-
// need to somehow transmute this back to @T. This was
139-
// originally stored into the map as:
140-
//
141-
// let data = @T;
142-
// let element = @data as @LocalData;
143-
// insert(key, element);
144-
//
145-
// This means that the element stored is a 2-word pair (because
146-
// it's a trait). The second element is the vtable (we don't
147-
// need it), and the first element is actually '@@T'. Not only
148-
// is this @@T, but it's a pointer to the base of the @@T (box
149-
// and all), so we have to traverse this to find the actual
150-
// pointer that we want.
151-
let (_vtable, box) =
152-
*cast::transmute::<&@LocalData, &(uint, *BoxRepr)>(data);
153-
let ptr: &@T = cast::transmute(&(*box).data);
154-
return Some((i, *ptr));
166+
Some((k, _, loans)) if k == key_value => {
167+
if loans != 0 {
168+
fail!("TLS value has been loaned via get already");
169+
}
170+
// Move the data out of the `entry` slot via util::replace. This
171+
// is guaranteed to succeed because we already matched on `Some`
172+
// above.
173+
let data = match util::replace(entry, None) {
174+
Some((_, data, _)) => data,
175+
None => libc::abort(),
176+
};
177+
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);
184+
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);
196+
197+
return Some(ret);
155198
}
156199
_ => {}
157200
}
158201
}
159202
return None;
160203
}
161204

162-
pub unsafe fn local_pop<T: 'static>(handle: Handle,
163-
key: LocalDataKey<T>) -> Option<@T> {
205+
pub unsafe fn local_get<T: 'static, U>(handle: Handle,
206+
key: LocalDataKey<T>,
207+
f: &fn(Option<&T>) -> U) -> U {
208+
// This does in theory take multiple mutable loans on the tls map, but the
209+
// references returned are never removed because the map is only increasing
210+
// in size (it never shrinks).
164211
let map = get_local_map(handle);
165-
match local_data_lookup(map, key) {
166-
Some((index, data)) => {
167-
map[index] = None;
168-
Some(data)
212+
let key_value = key_to_key_value(key);
213+
for map.mut_iter().advance |entry| {
214+
match *entry {
215+
Some((k, ref data, ref mut loans)) if k == key_value => {
216+
*loans = *loans + 1;
217+
let (_, val) = transmute_back(data);
218+
let ret = f(Some(val));
219+
*loans = *loans - 1;
220+
return ret;
221+
}
222+
_ => {}
169223
}
170-
None => None
171-
}
172-
}
173-
174-
pub unsafe fn local_get<T: 'static>(handle: Handle,
175-
key: LocalDataKey<T>) -> Option<@T> {
176-
match local_data_lookup(get_local_map(handle), key) {
177-
Some((_, data)) => Some(data),
178-
None => None
179224
}
225+
return f(None);
180226
}
181227

228+
// FIXME(#7673): This shouldn't require '@', it should use '~'
182229
pub unsafe fn local_set<T: 'static>(handle: Handle,
183-
key: LocalDataKey<T>,
230+
key: LocalDataKey<@T>,
184231
data: @T) {
185232
let map = get_local_map(handle);
186233
let keyval = key_to_key_value(key);
@@ -191,16 +238,31 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
191238
// everything to a trait (LocalData) which is then stored inside the map.
192239
// Upon destruction of the map, all the objects will be destroyed and the
193240
// traits have enough information about them to destroy themselves.
194-
let entry = Some((keyval, @data as @LocalData));
195-
196-
match local_data_lookup(map, key) {
197-
Some((index, _)) => { map[index] = entry; }
198-
None => {
199-
// Find an empty slot. If not, grow the vector.
200-
match map.iter().position(|x| x.is_none()) {
201-
Some(empty_index) => { map[empty_index] = entry; }
202-
None => { map.push(entry); }
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");
249+
}
250+
util::replace(value, data);
251+
return;
252+
}
253+
_ => {}
254+
}
255+
}
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;
203263
}
204264
}
205265
}
266+
// Finally push it on the end of the list
267+
map.push(Some((keyval, data, 0)));
206268
}

src/libstd/task/spawn.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -477,26 +477,28 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
477477
* Step 1. Get spawner's taskgroup info.
478478
*##################################################################*/
479479
let spawner_group: @@mut TCB =
480-
match local_get(OldHandle(spawner), taskgroup_key!()) {
481-
None => {
482-
// Main task, doing first spawn ever. Lazily initialise
483-
// here.
484-
let mut members = new_taskset();
485-
taskset_insert(&mut members, spawner);
486-
let tasks = exclusive(Some(TaskGroupData {
487-
members: members,
488-
descendants: new_taskset(),
489-
}));
490-
// Main task/group has no ancestors, no notifier, etc.
491-
let group = @@mut TCB(spawner,
492-
tasks,
493-
AncestorList(None),
494-
true,
495-
None);
496-
local_set(OldHandle(spawner), taskgroup_key!(), group);
497-
group
480+
do local_get(OldHandle(spawner), taskgroup_key!()) |group| {
481+
match group {
482+
None => {
483+
// Main task, doing first spawn ever. Lazily initialise
484+
// here.
485+
let mut members = new_taskset();
486+
taskset_insert(&mut members, spawner);
487+
let tasks = exclusive(Some(TaskGroupData {
488+
members: members,
489+
descendants: new_taskset(),
490+
}));
491+
// Main task/group has no ancestors, no notifier, etc.
492+
let group = @@mut TCB(spawner,
493+
tasks,
494+
AncestorList(None),
495+
true,
496+
None);
497+
local_set(OldHandle(spawner), taskgroup_key!(), group);
498+
group
499+
}
500+
Some(&group) => group
498501
}
499-
Some(group) => group
500502
};
501503
let spawner_group: &mut TCB = *spawner_group;
502504

0 commit comments

Comments
 (0)