11
11
#[ allow( missing_doc) ] ;
12
12
13
13
use cast;
14
- use cmp:: Eq ;
15
14
use libc;
16
15
use local_data:: LocalDataKey ;
17
16
use prelude:: * ;
@@ -44,25 +43,19 @@ impl Handle {
44
43
}
45
44
}
46
45
47
- pub trait LocalData { }
48
- impl < T : ' static > LocalData for @ T { }
46
+ trait LocalData { }
47
+ impl < T : ' static > LocalData for T { }
49
48
50
- impl Eq for @LocalData {
51
- fn eq ( & self , other : & @LocalData ) -> bool {
52
- unsafe {
53
- let ptr_a: & ( uint , uint ) = cast:: transmute ( self ) ;
54
- let ptr_b: & ( uint , uint ) = cast:: transmute ( other) ;
55
- return ptr_a == ptr_b;
56
- }
57
- }
58
- fn ne ( & self , other : & @LocalData ) -> bool { !( * self ) . eq ( other) }
59
- }
60
-
61
- // If TLS is used heavily in future, this could be made more efficient with a
62
- // proper map.
63
- type TaskLocalElement = ( * libc:: c_void , * libc:: c_void , @LocalData ) ;
64
- // Has to be a pointer at outermost layer; the foreign call returns void *.
65
- type TaskLocalMap = ~[ Option < TaskLocalElement > ] ;
49
+ // 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.
53
+ //
54
+ // n.b. Has to be a pointer at outermost layer; the foreign call returns void *.
55
+ //
56
+ // n.b. If TLS is used heavily in future, this could be made more efficient with
57
+ // a proper map.
58
+ type TaskLocalMap = ~[ Option < ( * libc:: c_void , @LocalData ) > ] ;
66
59
67
60
fn cleanup_task_local_map ( map_ptr : * libc:: c_void ) {
68
61
unsafe {
@@ -76,53 +69,52 @@ fn cleanup_task_local_map(map_ptr: *libc::c_void) {
76
69
77
70
// Gets the map from the runtime. Lazily initialises if not done so already.
78
71
unsafe fn get_local_map ( handle : Handle ) -> & mut TaskLocalMap {
79
- match handle {
80
- OldHandle ( task) => get_task_local_map ( task) ,
81
- NewHandle ( local_storage) => get_newsched_local_map ( local_storage)
82
- }
83
- }
84
72
85
- unsafe fn get_task_local_map ( task : * rust_task ) -> & mut TaskLocalMap {
73
+ unsafe fn oldsched_map ( task : * rust_task ) -> & mut TaskLocalMap {
74
+ extern fn cleanup_extern_cb ( map_ptr : * libc:: c_void ) {
75
+ cleanup_task_local_map ( map_ptr) ;
76
+ }
86
77
87
- extern fn cleanup_task_local_map_extern_cb ( map_ptr : * libc:: c_void ) {
88
- cleanup_task_local_map ( map_ptr) ;
78
+ // Relies on the runtime initialising the pointer to null.
79
+ // Note: the map is an owned pointer and is "owned" by TLS. It is moved
80
+ // into the tls slot for this task, and then mutable loans are taken
81
+ // from this slot to modify the map.
82
+ let map_ptr = rt:: rust_get_task_local_data ( task) ;
83
+ if ( * map_ptr) . is_null ( ) {
84
+ // First time TLS is used, create a new map and set up the necessary
85
+ // TLS information for its safe destruction
86
+ let map: TaskLocalMap = ~[ ] ;
87
+ * map_ptr = cast:: transmute ( map) ;
88
+ rt:: rust_task_local_data_atexit ( task, cleanup_extern_cb) ;
89
+ }
90
+ return cast:: transmute ( map_ptr) ;
89
91
}
90
92
91
- // Relies on the runtime initialising the pointer to null.
92
- // Note: the map is an owned pointer and is "owned" by TLS. It is moved
93
- // into the tls slot for this task, and then mutable loans are taken from
94
- // this slot to modify the map.
95
- let map_ptr = rt:: rust_get_task_local_data ( task) ;
96
- if ( * map_ptr) . is_null ( ) {
97
- // First time TLS is used, create a new map and set up the necessary
98
- // TLS information for its safe destruction
99
- let map: TaskLocalMap = ~[ ] ;
100
- * map_ptr = cast:: transmute ( map) ;
101
- rt:: rust_task_local_data_atexit ( task, cleanup_task_local_map_extern_cb) ;
93
+ unsafe fn newsched_map ( local : * mut LocalStorage ) -> & mut TaskLocalMap {
94
+ // This is based on the same idea as the oldsched code above.
95
+ match & mut * local {
96
+ // If the at_exit function is already set, then we just need to take
97
+ // a loan out on the TLS map stored inside
98
+ & LocalStorage ( ref mut map_ptr, Some ( _) ) => {
99
+ assert ! ( map_ptr. is_not_null( ) ) ;
100
+ return cast:: transmute ( map_ptr) ;
101
+ }
102
+ // If this is the first time we've accessed TLS, perform similar
103
+ // actions to the oldsched way of doing things.
104
+ & LocalStorage ( ref mut map_ptr, ref mut at_exit) => {
105
+ assert ! ( map_ptr. is_null( ) ) ;
106
+ assert ! ( at_exit. is_none( ) ) ;
107
+ let map: TaskLocalMap = ~[ ] ;
108
+ * map_ptr = cast:: transmute ( map) ;
109
+ * at_exit = Some ( cleanup_task_local_map) ;
110
+ return cast:: transmute ( map_ptr) ;
111
+ }
112
+ }
102
113
}
103
- return cast:: transmute ( map_ptr) ;
104
- }
105
114
106
- unsafe fn get_newsched_local_map ( local : * mut LocalStorage ) -> & mut TaskLocalMap {
107
- // This is based on the same idea as the oldsched code above.
108
- match & mut * local {
109
- // If the at_exit function is already set, then we just need to take a
110
- // loan out on the TLS map stored inside
111
- & LocalStorage ( ref mut map_ptr, Some ( _) ) => {
112
- assert ! ( map_ptr. is_not_null( ) ) ;
113
- return cast:: transmute ( map_ptr) ;
114
- }
115
- // If this is the first time we've accessed TLS, perform similar
116
- // actions to the oldsched way of doing things.
117
- & LocalStorage ( ref mut map_ptr, ref mut at_exit) => {
118
- assert ! ( map_ptr. is_null( ) ) ;
119
- assert ! ( at_exit. is_none( ) ) ;
120
- let map: TaskLocalMap = ~[ ] ;
121
- * map_ptr = cast:: transmute ( map) ;
122
- let at_exit_fn: ~fn ( * libc:: c_void ) = |p| cleanup_task_local_map ( p) ;
123
- * at_exit = Some ( at_exit_fn) ;
124
- return cast:: transmute ( map_ptr) ;
125
- }
115
+ match handle {
116
+ OldHandle ( task) => oldsched_map ( task) ,
117
+ NewHandle ( local_storage) => newsched_map ( local_storage)
126
118
}
127
119
}
128
120
@@ -132,95 +124,83 @@ unsafe fn key_to_key_value<T: 'static>(key: LocalDataKey<T>) -> *libc::c_void {
132
124
}
133
125
134
126
// If returning Some(..), returns with @T with the map's reference. Careful!
135
- unsafe fn local_data_lookup< T : ' static > (
136
- map : & mut TaskLocalMap , key : LocalDataKey < T > )
137
- -> Option < ( uint , * libc:: c_void ) > {
127
+ unsafe fn local_data_lookup < T : ' static > ( map : & TaskLocalMap ,
128
+ key : LocalDataKey < T > )
129
+ -> Option < ( uint , @T ) >
130
+ {
131
+ use managed:: raw:: BoxRepr ;
138
132
139
133
let key_value = key_to_key_value ( key) ;
140
134
for map. iter( ) . enumerate( ) . advance |( i, entry) | {
141
135
match * entry {
142
- Some ( ( k, data, _) ) if k == key_value => { return Some ( ( i, data) ) ; }
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) ) ;
155
+ }
143
156
_ => { }
144
157
}
145
158
}
146
159
return None ;
147
160
}
148
161
149
- unsafe fn local_get_helper < T : ' static > (
150
- handle: Handle , key: LocalDataKey < T > ,
151
- do_pop: bool) -> Option <@T > {
152
-
162
+ pub unsafe fn local_pop < T : ' static > ( handle : Handle ,
163
+ key : LocalDataKey < T > ) -> Option < @T > {
153
164
let map = get_local_map ( handle) ;
154
- // Interpreturn our findings from the map
155
- do local_data_lookup ( map, key) . map |result| {
156
- // A reference count magically appears on 'data' out of thin air. It
157
- // was referenced in the local_data box, though, not here, so before
158
- // overwriting the local_data_box we need to give an extra reference.
159
- // We must also give an extra reference when not removing.
160
- let ( index, data_ptr) = * result;
161
- let data: @T = cast:: transmute ( data_ptr) ;
162
- cast:: bump_box_refcount ( data) ;
163
- if do_pop {
165
+ match local_data_lookup ( map, key) {
166
+ Some ( ( index, data) ) => {
164
167
map[ index] = None ;
168
+ Some ( data)
165
169
}
166
- data
170
+ None => None
167
171
}
168
172
}
169
173
170
-
171
- pub unsafe fn local_pop < T : ' static > (
172
- handle : Handle ,
173
- key : LocalDataKey < T > ) -> Option < @T > {
174
-
175
- local_get_helper ( handle, key, true )
176
- }
177
-
178
- pub unsafe fn local_get < T : ' static > (
179
- handle : Handle ,
180
- key : LocalDataKey < T > ) -> Option < @T > {
181
-
182
- local_get_helper ( handle, key, false )
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
179
+ }
183
180
}
184
181
185
- pub unsafe fn local_set < T : ' static > (
186
- handle : Handle , key : LocalDataKey < T > , data : @ T ) {
187
-
182
+ pub unsafe fn local_set < T : ' static > ( handle : Handle ,
183
+ key : LocalDataKey < T > ,
184
+ data : @ T ) {
188
185
let map = get_local_map ( handle) ;
189
- // Store key+data as *voids. Data is invisibly referenced once; key isn't.
190
186
let keyval = key_to_key_value ( key) ;
191
- // We keep the data in two forms: one as an unsafe pointer, so we can get
192
- // it back by casting; another in an existential box, so the reference we
193
- // own on it can be dropped when the box is destroyed. The unsafe pointer
194
- // does not have a reference associated with it, so it may become invalid
195
- // when the box is destroyed.
196
- let data_ptr = * cast:: transmute :: < & @T , & * libc:: c_void > ( & data) ;
197
- let data_box = @data as @LocalData ;
198
- // Construct new entry to store in the map.
199
- let new_entry = Some ( ( keyval, data_ptr, data_box) ) ;
200
- // Find a place to put it.
187
+
188
+ // When the task-local map is destroyed, all the data needs to be cleaned
189
+ // up. For this reason we can't do some clever tricks to store '@T' as a
190
+ // '*c_void' or something like that. To solve the problem, we cast
191
+ // everything to a trait (LocalData) which is then stored inside the map.
192
+ // Upon destruction of the map, all the objects will be destroyed and the
193
+ // traits have enough information about them to destroy themselves.
194
+ let entry = Some ( ( keyval, @data as @LocalData ) ) ;
195
+
201
196
match local_data_lookup ( map, key) {
202
- Some ( ( index, _old_data_ptr) ) => {
203
- // Key already had a value set, _old_data_ptr, whose reference
204
- // will get dropped when the local_data box is overwritten.
205
- map[ index] = new_entry;
206
- }
197
+ Some ( ( index, _) ) => { map[ index] = entry; }
207
198
None => {
208
199
// Find an empty slot. If not, grow the vector.
209
200
match map. iter ( ) . position ( |x| x. is_none ( ) ) {
210
- Some ( empty_index) => { map[ empty_index] = new_entry ; }
211
- None => { map. push ( new_entry ) ; }
201
+ Some ( empty_index) => { map[ empty_index] = entry ; }
202
+ None => { map. push ( entry ) ; }
212
203
}
213
204
}
214
205
}
215
206
}
216
-
217
- pub unsafe fn local_modify < T : ' static > (
218
- handle : Handle , key : LocalDataKey < T > ,
219
- modify_fn : & fn ( Option < @T > ) -> Option < @T > ) {
220
-
221
- // Could be more efficient by doing the lookup work, but this is easy.
222
- let newdata = modify_fn ( local_pop ( handle, key) ) ;
223
- if newdata. is_some ( ) {
224
- local_set ( handle, key, newdata. unwrap ( ) ) ;
225
- }
226
- }
0 commit comments