Skip to content

Commit 1c0b457

Browse files
committed
Workaround #2912: Implement existential TLS and cheat with taskgroup key
1 parent d338879 commit 1c0b457

File tree

1 file changed

+41
-39
lines changed

1 file changed

+41
-39
lines changed

src/libcore/task.rs

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,6 @@ class taskgroup {
613613
}
614614
}
615615

616-
fn taskgroup_key(+_group: @taskgroup) { } // For TLS
617-
618616
fn enlist_in_taskgroup(group_arc: taskgroup_arc,
619617
me: *rust_task) -> option<uint> {
620618
do group_arc.with |_c, state| {
@@ -682,9 +680,16 @@ fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint,
682680
};
683681
}
684682

683+
// FIXME (#2912): Work around core-vs-coretest function duplication. Can't use
684+
// a proper closure because the #[test]s won't understand. Have to fake it.
685+
unsafe fn taskgroup_key() -> local_data_key<taskgroup> {
686+
// Use a "code pointer" value that will never be a real code pointer.
687+
unsafe::transmute((-2 as uint, 0u))
688+
}
689+
685690
fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
686691
let me = rustrt::rust_get_task();
687-
alt unsafe { local_get(me, taskgroup_key) } {
692+
alt unsafe { local_get(me, taskgroup_key()) } {
688693
some(group) {
689694
// Clone the shared state for the child; propagate main-ness.
690695
(group.tasks.clone(), group.is_main)
@@ -693,7 +698,7 @@ fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
693698
// Main task, doing first spawn ever.
694699
let tasks = arc::exclusive(some(dvec::from_elem(some(me))));
695700
let group = @taskgroup(tasks.clone(), me, 0, true);
696-
unsafe { local_set(me, taskgroup_key, group); }
701+
unsafe { local_set(me, taskgroup_key(), group); }
697702
// Tell child task it's also in the main group.
698703
(tasks, true)
699704
}
@@ -764,12 +769,13 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
764769
some(my_index) {
765770
let group =
766771
@taskgroup(child_tg, child_task, my_index, is_main);
767-
unsafe { local_set(child_task, taskgroup_key, group); }
772+
unsafe { local_set(child_task, taskgroup_key(), group); }
768773
// Run the child's body.
769774
f();
770775
// TLS cleanup code will exit the taskgroup.
771776
}
772-
none { }
777+
none {
778+
}
773779
}
774780
}
775781
}
@@ -820,34 +826,30 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
820826
****************************************************************************/
821827

822828
/**
823-
* Indexes a task-local data slot. The function itself is used to
824-
* automatically finalise stored values; also, its code pointer is used for
829+
* Indexes a task-local data slot. The function's code pointer is used for
825830
* comparison. Recommended use is to write an empty function for each desired
826-
* task-local data slot (and use class destructors, instead of code inside the
827-
* finaliser, if specific teardown is needed). DO NOT use multiple
831+
* task-local data slot (and use class destructors, not code inside the
832+
* function, if specific teardown is needed). DO NOT use multiple
828833
* instantiations of a single polymorphic function to index data of different
829834
* types; arbitrary type coercion is possible this way. The interface is safe
830835
* as long as all key functions are monomorphic.
831836
*/
832837
type local_data_key<T> = fn@(+@T);
833838

839+
iface local_data { }
840+
impl<T> of local_data for @T { }
841+
834842
// We use dvec because it's the best data structure in core. If TLS is used
835843
// heavily in future, this could be made more efficient with a proper map.
836-
type task_local_element = (*libc::c_void, *libc::c_void, fn@(+*libc::c_void));
844+
type task_local_element = (*libc::c_void, *libc::c_void, local_data);
837845
// Has to be a pointer at outermost layer; the foreign call returns void *.
838846
type task_local_map = @dvec::dvec<option<task_local_element>>;
839847

840848
extern fn cleanup_task_local_map(map_ptr: *libc::c_void) unsafe {
841849
assert !map_ptr.is_null();
842850
// Get and keep the single reference that was created at the beginning.
843-
let map: task_local_map = unsafe::reinterpret_cast(map_ptr);
844-
for (*map).each |entry| {
845-
alt entry {
846-
// Finaliser drops data. We drop the finaliser implicitly here.
847-
some((_key, data, finalise_fn)) { finalise_fn(data); }
848-
none { }
849-
}
850-
}
851+
let _map: task_local_map = unsafe::reinterpret_cast(map_ptr);
852+
// All local_data will be destroyed along with the map.
851853
}
852854

853855
// Gets the map from the runtime. Lazily initialises if not done so already.
@@ -881,15 +883,15 @@ unsafe fn key_to_key_value<T>(key: local_data_key<T>) -> *libc::c_void {
881883

882884
// If returning some(..), returns with @T with the map's reference. Careful!
883885
unsafe fn local_data_lookup<T>(map: task_local_map, key: local_data_key<T>)
884-
-> option<(uint, *libc::c_void, fn@(+*libc::c_void))> {
886+
-> option<(uint, *libc::c_void)> {
885887
let key_value = key_to_key_value(key);
886888
let map_pos = (*map).position(|entry|
887889
alt entry { some((k,_,_)) { k == key_value } none { false } }
888890
);
889891
do map_pos.map |index| {
890892
// .get() is guaranteed because of "none { false }" above.
891-
let (_, data_ptr, finaliser) = (*map)[index].get();
892-
(index, data_ptr, finaliser)
893+
let (_, data_ptr, _) = (*map)[index].get();
894+
(index, data_ptr)
893895
}
894896
}
895897

@@ -898,15 +900,15 @@ unsafe fn local_get_helper<T>(task: *rust_task, key: local_data_key<T>,
898900
let map = get_task_local_map(task);
899901
// Interpret our findings from the map
900902
do local_data_lookup(map, key).map |result| {
901-
// A reference count magically appears on 'data' out of thin air.
902-
// 'data' has the reference we originally stored it with. We either
903-
// need to erase it from the map or artificially bump the count.
904-
let (index, data_ptr, _) = result;
903+
// A reference count magically appears on 'data' out of thin air. It
904+
// was referenced in the local_data box, though, not here, so before
905+
// overwriting the local_data_box we need to give an extra reference.
906+
// We must also give an extra reference when not removing.
907+
let (index, data_ptr) = result;
905908
let data: @T = unsafe::transmute(data_ptr);
909+
unsafe::bump_box_refcount(data);
906910
if do_pop {
907911
(*map).set_elt(index, none);
908-
} else {
909-
unsafe::bump_box_refcount(data);
910912
}
911913
data
912914
}
@@ -926,20 +928,20 @@ unsafe fn local_set<T>(task: *rust_task, key: local_data_key<T>, +data: @T) {
926928
let map = get_task_local_map(task);
927929
// Store key+data as *voids. Data is invisibly referenced once; key isn't.
928930
let keyval = key_to_key_value(key);
929-
let data_ptr = unsafe::transmute(data);
930-
// Finaliser is called at task exit to de-reference up remaining entries.
931-
let finaliser: fn@(+*libc::c_void) = unsafe::reinterpret_cast(key);
931+
// We keep the data in two forms: one as an unsafe pointer, so we can get
932+
// it back by casting; another in an existential box, so the reference we
933+
// own on it can be dropped when the box is destroyed. The unsafe pointer
934+
// does not have a reference associated with it, so it may become invalid
935+
// when the box is destroyed.
936+
let data_ptr = unsafe::reinterpret_cast(data);
937+
let data_box = data as local_data;
932938
// Construct new entry to store in the map.
933-
let new_entry = some((keyval, data_ptr, finaliser));
939+
let new_entry = some((keyval, data_ptr, data_box));
934940
// Find a place to put it.
935941
alt local_data_lookup(map, key) {
936-
some((index, old_data_ptr, old_finaliser)) {
937-
// Key already had a value set, old_data_ptr, whose reference we
938-
// need to drop. After that, overwriting its slot will be safe.
939-
// (The heap-allocated finaliser will be freed in the overwrite.)
940-
// FIXME(#2734): just transmuting old_data_ptr to @T doesn't work,
941-
// similarly to the sample there (but more our/unsafety's fault?).
942-
old_finaliser(old_data_ptr);
942+
some((index, _old_data_ptr)) {
943+
// Key already had a value set, _old_data_ptr, whose reference
944+
// will get dropped when the local_data box is overwritten.
943945
(*map).set_elt(index, new_entry);
944946
}
945947
none {

0 commit comments

Comments
 (0)