From f996b3ae8169d699362622e4530d014659628108 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 19 Sep 2012 17:37:09 -0700 Subject: [PATCH] core: Split local_data into local_data/local_data_priv --- src/libcore/core.rc | 1 + src/libcore/task.rs | 7 +- src/libcore/task/local_data.rs | 167 ++-------------------------- src/libcore/task/local_data_priv.rs | 157 ++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 163 deletions(-) create mode 100644 src/libcore/task/local_data_priv.rs diff --git a/src/libcore/core.rc b/src/libcore/core.rc index 9bb13af5a82..3a93d069ab8 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -221,6 +221,7 @@ mod send_map; mod comm; mod task { mod local_data; + mod local_data_priv; } mod future; mod pipes; diff --git a/src/libcore/task.rs b/src/libcore/task.rs index 788802a5fe9..1660a94e3f1 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -30,6 +30,7 @@ use cmp::Eq; use result::Result; use pipes::{stream, Chan, Port}; +use local_data_priv::{local_get, local_set}; export Task; export TaskResult; @@ -1209,7 +1210,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool) /*######################################################################* * Step 1. Get spawner's taskgroup info. *######################################################################*/ - let spawner_group = match unsafe { local_data::local_get(spawner, + let spawner_group = match unsafe { local_get(spawner, taskgroup_key!()) } { None => { // Main task, doing first spawn ever. Lazily initialise here. @@ -1222,7 +1223,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool) let group = @TCB(spawner, move tasks, AncestorList(None), true, None); unsafe { - local_data::local_set(spawner, taskgroup_key!(), group); + local_set(spawner, taskgroup_key!(), group); } group } @@ -1351,7 +1352,7 @@ fn spawn_raw(+opts: TaskOpts, +f: fn~()) { let group = @TCB(child, move child_arc, move ancestors, is_main, move notifier); unsafe { - local_data::local_set(child, taskgroup_key!(), group); + local_set(child, taskgroup_key!(), group); } // Run the child's body. diff --git a/src/libcore/task/local_data.rs b/src/libcore/task/local_data.rs index 4fbf0475c0a..eefdb9eceee 100644 --- a/src/libcore/task/local_data.rs +++ b/src/libcore/task/local_data.rs @@ -14,15 +14,18 @@ Casting 'Arcane Sight' reveals an overwhelming aura of Transmutation magic. */ -export local_data_key; +export LocalDataKey; export local_data_pop; export local_data_get; export local_data_set; export local_data_modify; -// XXX: These shouldn't be exported but they are used by task.rs -export local_get; -export local_set; +use local_data_priv::{ + local_pop, + local_get, + local_set, + local_modify +}; /** * Indexes a task-local data slot. The function's code pointer is used for @@ -40,162 +43,6 @@ export local_set; */ type LocalDataKey = &fn(+@T); -trait LocalData { } -impl @T: LocalData { } - -impl LocalData: Eq { - pure fn eq(&&other: LocalData) -> bool unsafe { - let ptr_a: (uint, uint) = cast::reinterpret_cast(&self); - let ptr_b: (uint, uint) = cast::reinterpret_cast(&other); - return ptr_a == ptr_b; - } - pure fn ne(&&other: LocalData) -> bool { !self.eq(other) } -} - -// We use dvec because it's the best data structure in core. If TLS is used -// heavily in future, this could be made more efficient with a proper map. -type TaskLocalElement = (*libc::c_void, *libc::c_void, LocalData); -// Has to be a pointer at outermost layer; the foreign call returns void *. -type TaskLocalMap = @dvec::DVec>; - -extern fn cleanup_task_local_map(map_ptr: *libc::c_void) unsafe { - assert !map_ptr.is_null(); - // Get and keep the single reference that was created at the beginning. - let _map: TaskLocalMap = cast::reinterpret_cast(&map_ptr); - // All local_data will be destroyed along with the map. -} - -// Gets the map from the runtime. Lazily initialises if not done so already. -unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { - - // Relies on the runtime initialising the pointer to null. - // NOTE: The map's box lives in TLS invisibly referenced once. Each time - // we retrieve it for get/set, we make another reference, which get/set - // drop when they finish. No "re-storing after modifying" is needed. - let map_ptr = rustrt::rust_get_task_local_data(task); - if map_ptr.is_null() { - let map: TaskLocalMap = @dvec::DVec(); - // Use reinterpret_cast -- transmute would take map away from us also. - rustrt::rust_set_task_local_data( - task, cast::reinterpret_cast(&map)); - rustrt::rust_task_local_data_atexit(task, cleanup_task_local_map); - // Also need to reference it an extra time to keep it for now. - cast::bump_box_refcount(map); - map - } else { - let map = cast::transmute(move map_ptr); - cast::bump_box_refcount(map); - map - } -} - -unsafe fn key_to_key_value( - key: LocalDataKey) -> *libc::c_void { - - // Keys are closures, which are (fnptr,envptr) pairs. Use fnptr. - // Use reintepret_cast -- transmute would leak (forget) the closure. - let pair: (*libc::c_void, *libc::c_void) = cast::reinterpret_cast(&key); - pair.first() -} - -// If returning Some(..), returns with @T with the map's reference. Careful! -unsafe fn local_data_lookup( - map: TaskLocalMap, key: LocalDataKey) - -> Option<(uint, *libc::c_void)> { - - let key_value = key_to_key_value(key); - let map_pos = (*map).position(|entry| - match entry { - Some((k,_,_)) => k == key_value, - None => false - } - ); - do map_pos.map |index| { - // .get() is guaranteed because of "None { false }" above. - let (_, data_ptr, _) = (*map)[index].get(); - (index, data_ptr) - } -} - -unsafe fn local_get_helper( - task: *rust_task, key: LocalDataKey, - do_pop: bool) -> Option<@T> { - - let map = get_task_local_map(task); - // Interpreturn our findings from the map - do local_data_lookup(map, key).map |result| { - // A reference count magically appears on 'data' out of thin air. It - // was referenced in the local_data box, though, not here, so before - // overwriting the local_data_box we need to give an extra reference. - // We must also give an extra reference when not removing. - let (index, data_ptr) = result; - let data: @T = cast::transmute(move data_ptr); - cast::bump_box_refcount(data); - if do_pop { - (*map).set_elt(index, None); - } - data - } -} - -unsafe fn local_pop( - task: *rust_task, - key: LocalDataKey) -> Option<@T> { - - local_get_helper(task, key, true) -} - -unsafe fn local_get( - task: *rust_task, - key: LocalDataKey) -> Option<@T> { - - local_get_helper(task, key, false) -} - -unsafe fn local_set( - task: *rust_task, key: LocalDataKey, +data: @T) { - - let map = get_task_local_map(task); - // Store key+data as *voids. Data is invisibly referenced once; key isn't. - let keyval = key_to_key_value(key); - // We keep the data in two forms: one as an unsafe pointer, so we can get - // it back by casting; another in an existential box, so the reference we - // own on it can be dropped when the box is destroyed. The unsafe pointer - // does not have a reference associated with it, so it may become invalid - // when the box is destroyed. - let data_ptr = cast::reinterpret_cast(&data); - let data_box = data as LocalData; - // Construct new entry to store in the map. - let new_entry = Some((keyval, data_ptr, data_box)); - // Find a place to put it. - match local_data_lookup(map, key) { - Some((index, _old_data_ptr)) => { - // Key already had a value set, _old_data_ptr, whose reference - // will get dropped when the local_data box is overwritten. - (*map).set_elt(index, new_entry); - } - None => { - // Find an empty slot. If not, grow the vector. - match (*map).position(|x| x.is_none()) { - Some(empty_index) => (*map).set_elt(empty_index, new_entry), - None => (*map).push(new_entry) - } - } - } -} - -unsafe fn local_modify( - task: *rust_task, key: LocalDataKey, - modify_fn: fn(Option<@T>) -> Option<@T>) { - - // Could be more efficient by doing the lookup work, but this is easy. - let newdata = modify_fn(local_pop(task, key)); - if newdata.is_some() { - local_set(task, key, option::unwrap(move newdata)); - } -} - -/* Exported interface for task-local data (plus local_data_key above). */ /** * Remove a task-local data value from the table, returning the * reference that was originally created to insert it. diff --git a/src/libcore/task/local_data_priv.rs b/src/libcore/task/local_data_priv.rs new file mode 100644 index 00000000000..640ea3577b3 --- /dev/null +++ b/src/libcore/task/local_data_priv.rs @@ -0,0 +1,157 @@ +use local_data::LocalDataKey; + +trait LocalData { } +impl @T: LocalData { } + +impl LocalData: Eq { + pure fn eq(&&other: LocalData) -> bool unsafe { + let ptr_a: (uint, uint) = cast::reinterpret_cast(&self); + let ptr_b: (uint, uint) = cast::reinterpret_cast(&other); + return ptr_a == ptr_b; + } + pure fn ne(&&other: LocalData) -> bool { !self.eq(other) } +} + +// We use dvec because it's the best data structure in core. If TLS is used +// heavily in future, this could be made more efficient with a proper map. +type TaskLocalElement = (*libc::c_void, *libc::c_void, LocalData); +// Has to be a pointer at outermost layer; the foreign call returns void *. +type TaskLocalMap = @dvec::DVec>; + +extern fn cleanup_task_local_map(map_ptr: *libc::c_void) unsafe { + assert !map_ptr.is_null(); + // Get and keep the single reference that was created at the beginning. + let _map: TaskLocalMap = cast::reinterpret_cast(&map_ptr); + // All local_data will be destroyed along with the map. +} + +// Gets the map from the runtime. Lazily initialises if not done so already. +unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { + + // Relies on the runtime initialising the pointer to null. + // NOTE: The map's box lives in TLS invisibly referenced once. Each time + // we retrieve it for get/set, we make another reference, which get/set + // drop when they finish. No "re-storing after modifying" is needed. + let map_ptr = rustrt::rust_get_task_local_data(task); + if map_ptr.is_null() { + let map: TaskLocalMap = @dvec::DVec(); + // Use reinterpret_cast -- transmute would take map away from us also. + rustrt::rust_set_task_local_data( + task, cast::reinterpret_cast(&map)); + rustrt::rust_task_local_data_atexit(task, cleanup_task_local_map); + // Also need to reference it an extra time to keep it for now. + cast::bump_box_refcount(map); + map + } else { + let map = cast::transmute(move map_ptr); + cast::bump_box_refcount(map); + map + } +} + +unsafe fn key_to_key_value( + key: LocalDataKey) -> *libc::c_void { + + // Keys are closures, which are (fnptr,envptr) pairs. Use fnptr. + // Use reintepret_cast -- transmute would leak (forget) the closure. + let pair: (*libc::c_void, *libc::c_void) = cast::reinterpret_cast(&key); + pair.first() +} + +// If returning Some(..), returns with @T with the map's reference. Careful! +unsafe fn local_data_lookup( + map: TaskLocalMap, key: LocalDataKey) + -> Option<(uint, *libc::c_void)> { + + let key_value = key_to_key_value(key); + let map_pos = (*map).position(|entry| + match entry { + Some((k,_,_)) => k == key_value, + None => false + } + ); + do map_pos.map |index| { + // .get() is guaranteed because of "None { false }" above. + let (_, data_ptr, _) = (*map)[index].get(); + (index, data_ptr) + } +} + +unsafe fn local_get_helper( + task: *rust_task, key: LocalDataKey, + do_pop: bool) -> Option<@T> { + + let map = get_task_local_map(task); + // Interpreturn our findings from the map + do local_data_lookup(map, key).map |result| { + // A reference count magically appears on 'data' out of thin air. It + // was referenced in the local_data box, though, not here, so before + // overwriting the local_data_box we need to give an extra reference. + // We must also give an extra reference when not removing. + let (index, data_ptr) = result; + let data: @T = cast::transmute(move data_ptr); + cast::bump_box_refcount(data); + if do_pop { + (*map).set_elt(index, None); + } + data + } +} + + +unsafe fn local_pop( + task: *rust_task, + key: LocalDataKey) -> Option<@T> { + + local_get_helper(task, key, true) +} + +unsafe fn local_get( + task: *rust_task, + key: LocalDataKey) -> Option<@T> { + + local_get_helper(task, key, false) +} + +unsafe fn local_set( + task: *rust_task, key: LocalDataKey, +data: @T) { + + let map = get_task_local_map(task); + // Store key+data as *voids. Data is invisibly referenced once; key isn't. + let keyval = key_to_key_value(key); + // We keep the data in two forms: one as an unsafe pointer, so we can get + // it back by casting; another in an existential box, so the reference we + // own on it can be dropped when the box is destroyed. The unsafe pointer + // does not have a reference associated with it, so it may become invalid + // when the box is destroyed. + let data_ptr = cast::reinterpret_cast(&data); + let data_box = data as LocalData; + // Construct new entry to store in the map. + let new_entry = Some((keyval, data_ptr, data_box)); + // Find a place to put it. + match local_data_lookup(map, key) { + Some((index, _old_data_ptr)) => { + // Key already had a value set, _old_data_ptr, whose reference + // will get dropped when the local_data box is overwritten. + (*map).set_elt(index, new_entry); + } + None => { + // Find an empty slot. If not, grow the vector. + match (*map).position(|x| x.is_none()) { + Some(empty_index) => (*map).set_elt(empty_index, new_entry), + None => (*map).push(new_entry) + } + } + } +} + +unsafe fn local_modify( + task: *rust_task, key: LocalDataKey, + modify_fn: fn(Option<@T>) -> Option<@T>) { + + // Could be more efficient by doing the lookup work, but this is easy. + let newdata = modify_fn(local_pop(task, key)); + if newdata.is_some() { + local_set(task, key, option::unwrap(move newdata)); + } +}