提交 bad1062c 编写于 作者: B bors

auto merge of #19094 : alexcrichton/rust/rm-std-local-data, r=aturon

This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: https://github.com/rust-lang/rfcs/pull/461
[breaking-change]
......@@ -171,7 +171,7 @@
extern crate regex;
use regex::Regex;
use std::cell::RefCell;
use std::fmt;
use std::io::LineBufferedWriter;
use std::io;
......@@ -181,6 +181,8 @@
use std::slice;
use std::sync::{Once, ONCE_INIT};
use regex::Regex;
use directive::LOG_LEVEL_NAMES;
pub mod macros;
......@@ -213,7 +215,9 @@
/// Error log level
pub const ERROR: u32 = 1;
local_data_key!(local_logger: Box<Logger + Send>)
thread_local!(static LOCAL_LOGGER: RefCell<Option<Box<Logger + Send>>> = {
RefCell::new(None)
})
/// A trait used to represent an interface to a task-local logger. Each task
/// can have its own custom logger which can respond to logging messages
......@@ -283,7 +287,9 @@ pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
// Completely remove the local logger from TLS in case anyone attempts to
// frob the slot while we're doing the logging. This will destroy any logger
// set during logging.
let mut logger = local_logger.replace(None).unwrap_or_else(|| {
let mut logger = LOCAL_LOGGER.with(|s| {
s.borrow_mut().take()
}).unwrap_or_else(|| {
box DefaultLogger { handle: io::stderr() } as Box<Logger + Send>
});
logger.log(&LogRecord {
......@@ -293,7 +299,7 @@ pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
module_path: loc.module_path,
line: loc.line,
});
local_logger.replace(Some(logger));
set_logger(logger);
}
/// Getter for the global log level. This is a function so that it can be called
......@@ -305,7 +311,10 @@ pub fn log_level() -> u32 { unsafe { LOG_LEVEL } }
/// Replaces the task-local logger with the specified logger, returning the old
/// logger.
pub fn set_logger(logger: Box<Logger + Send>) -> Option<Box<Logger + Send>> {
local_logger.replace(Some(logger))
let mut l = Some(logger);
LOCAL_LOGGER.with(|slot| {
mem::replace(&mut *slot.borrow_mut(), l.take())
})
}
/// A LogRecord is created by the logging macros, and passed as the only
......
......@@ -10,7 +10,7 @@
#![allow(non_camel_case_types)]
use std::cell::RefCell;
use std::cell::{RefCell, Cell};
use std::collections::HashMap;
use std::fmt::Show;
use std::hash::{Hash, Hasher};
......@@ -26,11 +26,14 @@
pub struct ErrorReported;
pub fn time<T, U>(do_it: bool, what: &str, u: U, f: |U| -> T) -> T {
local_data_key!(depth: uint);
thread_local!(static DEPTH: Cell<uint> = Cell::new(0));
if !do_it { return f(u); }
let old = depth.get().map(|d| *d).unwrap_or(0);
depth.replace(Some(old + 1));
let old = DEPTH.with(|slot| {
let r = slot.get();
slot.set(r + 1);
r
});
let mut u = Some(u);
let mut rv = None;
......@@ -41,7 +44,7 @@ pub fn time<T, U>(do_it: bool, what: &str, u: U, f: |U| -> T) -> T {
println!("{}time: {}.{:03} \t{}", " ".repeat(old),
dur.num_seconds(), dur.num_milliseconds() % 1000, what);
depth.replace(Some(old));
DEPTH.with(|slot| slot.set(old));
rv
}
......
......@@ -100,17 +100,20 @@
use syntax::visit;
use syntax::{ast, ast_util, ast_map};
local_data_key!(task_local_insn_key: RefCell<Vec<&'static str>>)
thread_local!(static TASK_LOCAL_INSN_KEY: RefCell<Option<Vec<&'static str>>> = {
RefCell::new(None)
})
pub fn with_insn_ctxt(blk: |&[&'static str]|) {
match task_local_insn_key.get() {
Some(ctx) => blk(ctx.borrow().as_slice()),
None => ()
}
TASK_LOCAL_INSN_KEY.with(|slot| {
slot.borrow().as_ref().map(|s| blk(s.as_slice()));
})
}
pub fn init_insn_ctxt() {
task_local_insn_key.replace(Some(RefCell::new(Vec::new())));
TASK_LOCAL_INSN_KEY.with(|slot| {
*slot.borrow_mut() = Some(Vec::new());
});
}
pub struct _InsnCtxt {
......@@ -120,19 +123,23 @@ pub struct _InsnCtxt {
#[unsafe_destructor]
impl Drop for _InsnCtxt {
fn drop(&mut self) {
match task_local_insn_key.get() {
Some(ctx) => { ctx.borrow_mut().pop(); }
None => {}
}
TASK_LOCAL_INSN_KEY.with(|slot| {
match slot.borrow_mut().as_mut() {
Some(ctx) => { ctx.pop(); }
None => {}
}
})
}
}
pub fn push_ctxt(s: &'static str) -> _InsnCtxt {
debug!("new InsnCtxt: {}", s);
match task_local_insn_key.get() {
Some(ctx) => ctx.borrow_mut().push(s),
None => {}
}
TASK_LOCAL_INSN_KEY.with(|slot| {
match slot.borrow_mut().as_mut() {
Some(ctx) => ctx.push(s),
None => {}
}
});
_InsnCtxt { _cannot_construct_outside_of_this_module: () }
}
......
......@@ -26,7 +26,7 @@
use html::item_type;
use html::item_type::ItemType;
use html::render;
use html::render::{cache_key, current_location_key};
use html::render::{cache, CURRENT_LOCATION_KEY};
/// Helper to render an optional visibility with a space after it (if the
/// visibility is preset)
......@@ -236,9 +236,9 @@ fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool,
generics.push_str("&gt;");
}
let loc = current_location_key.get().unwrap();
let cache = cache_key.get().unwrap();
let abs_root = root(&**cache, loc.as_slice());
let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
let cache = cache();
let abs_root = root(&*cache, loc.as_slice());
let rel_root = match path.segments[0].name.as_slice() {
"self" => Some("./".to_string()),
_ => None,
......@@ -271,7 +271,7 @@ fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool,
}
}
match info(&**cache) {
match info(&*cache) {
// This is a documented path, link to it!
Some((ref fqp, shortty)) if abs_root.is_some() => {
let mut url = String::from_str(abs_root.unwrap().as_slice());
......@@ -308,12 +308,12 @@ fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool,
fn primitive_link(f: &mut fmt::Formatter,
prim: clean::PrimitiveType,
name: &str) -> fmt::Result {
let m = cache_key.get().unwrap();
let m = cache();
let mut needs_termination = false;
match m.primitive_locations.get(&prim) {
Some(&ast::LOCAL_CRATE) => {
let loc = current_location_key.get().unwrap();
let len = if loc.len() == 0 {0} else {loc.len() - 1};
let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
let len = if len == 0 {0} else {len - 1};
try!(write!(f, "<a href='{}primitive.{}.html'>",
"../".repeat(len),
prim.to_url_str()));
......@@ -327,8 +327,8 @@ fn primitive_link(f: &mut fmt::Formatter,
let loc = match m.extern_locations[cnum] {
render::Remote(ref s) => Some(s.to_string()),
render::Local => {
let loc = current_location_key.get().unwrap();
Some("../".repeat(loc.len()))
let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
Some("../".repeat(len))
}
render::Unknown => None,
};
......@@ -371,12 +371,10 @@ impl fmt::Show for clean::Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
clean::TyParamBinder(id) => {
let m = cache_key.get().unwrap();
f.write(m.typarams[ast_util::local_def(id)].as_bytes())
f.write(cache().typarams[ast_util::local_def(id)].as_bytes())
}
clean::Generic(did) => {
let m = cache_key.get().unwrap();
f.write(m.typarams[did].as_bytes())
f.write(cache().typarams[did].as_bytes())
}
clean::ResolvedPath{ did, ref typarams, ref path } => {
try!(resolved_path(f, did, path, false));
......
......@@ -147,10 +147,14 @@ fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> {
}
}
local_data_key!(used_header_map: RefCell<HashMap<String, uint>>)
local_data_key!(test_idx: Cell<uint>)
// None == render an example, but there's no crate name
local_data_key!(pub playground_krate: Option<String>)
thread_local!(static USED_HEADER_MAP: RefCell<HashMap<String, uint>> = {
RefCell::new(HashMap::new())
})
thread_local!(static TEST_IDX: Cell<uint> = Cell::new(0))
thread_local!(pub static PLAYGROUND_KRATE: RefCell<Option<Option<String>>> = {
RefCell::new(None)
})
pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
extern fn block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer,
......@@ -183,12 +187,15 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
stripped_filtered_line(*l).is_none()
});
let text = lines.collect::<Vec<&str>>().connect("\n");
if !rendered {
if rendered { return }
PLAYGROUND_KRATE.with(|krate| {
let mut s = String::new();
let id = playground_krate.get().map(|krate| {
let idx = test_idx.get().unwrap();
let i = idx.get();
idx.set(i + 1);
let id = krate.borrow().as_ref().map(|krate| {
let idx = TEST_IDX.with(|slot| {
let i = slot.get();
slot.set(i + 1);
i
});
let test = origtext.lines().map(|l| {
stripped_filtered_line(l).unwrap_or(l)
......@@ -197,15 +204,15 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
let test = test::maketest(test.as_slice(), krate, false, false);
s.push_str(format!("<span id='rust-example-raw-{}' \
class='rusttest'>{}</span>",
i, Escape(test.as_slice())).as_slice());
format!("rust-example-rendered-{}", i)
idx, Escape(test.as_slice())).as_slice());
format!("rust-example-rendered-{}", idx)
});
let id = id.as_ref().map(|a| a.as_slice());
s.push_str(highlight::highlight(text.as_slice(), None, id)
.as_slice());
let output = s.to_c_str();
hoedown_buffer_puts(ob, output.as_ptr());
}
})
}
}
......@@ -229,18 +236,20 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
// This is a terrible hack working around how hoedown gives us rendered
// html for text rather than the raw text.
let id = id.replace("<code>", "").replace("</code>", "").to_string();
let opaque = opaque as *mut hoedown_html_renderer_state;
let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) };
// Make sure our hyphenated ID is unique for this page
let map = used_header_map.get().unwrap();
let id = match map.borrow_mut().get_mut(&id) {
None => id,
Some(a) => { *a += 1; format!("{}-{}", id, *a - 1) }
};
map.borrow_mut().insert(id.clone(), 1);
let id = USED_HEADER_MAP.with(|map| {
let id = id.replace("<code>", "").replace("</code>", "").to_string();
let id = match map.borrow_mut().get_mut(&id) {
None => id,
Some(a) => { *a += 1; format!("{}-{}", id, *a - 1) }
};
map.borrow_mut().insert(id.clone(), 1);
id
});
let sec = match opaque.toc_builder {
Some(ref mut builder) => {
......@@ -262,9 +271,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
text.with_c_str(|p| unsafe { hoedown_buffer_puts(ob, p) });
}
if used_header_map.get().is_none() {
reset_headers();
}
reset_headers();
unsafe {
let ob = hoedown_buffer_new(DEF_OUNIT);
......@@ -418,8 +425,8 @@ fn parse(string: &str) -> LangString {
/// used at the beginning of rendering an entire HTML page to reset from the
/// previous state (if any).
pub fn reset_headers() {
used_header_map.replace(Some(RefCell::new(HashMap::new())));
test_idx.replace(Some(Cell::new(0)));
USED_HEADER_MAP.with(|s| s.borrow_mut().clear());
TEST_IDX.with(|s| s.set(0));
}
impl<'a> fmt::Show for Markdown<'a> {
......
......@@ -34,8 +34,10 @@
//! both occur before the crate is rendered.
pub use self::ExternalLocation::*;
use std::collections::{HashMap, HashSet};
use std::cell::RefCell;
use std::collections::hash_map::{Occupied, Vacant};
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::fmt;
use std::io::fs::PathExtensions;
use std::io::{fs, File, BufferedWriter, BufferedReader};
......@@ -141,6 +143,7 @@ pub struct Impl {
/// to be a fairly large and expensive structure to clone. Instead this adheres
/// to `Send` so it may be stored in a `Arc` instance and shared among the various
/// rendering tasks.
#[deriving(Default)]
pub struct Cache {
/// Mapping of typaram ids to the name of the type parameter. This is used
/// when pretty-printing a type (so pretty printing doesn't have to
......@@ -235,8 +238,9 @@ struct IndexItem {
// TLS keys used to carry information around during rendering.
local_data_key!(pub cache_key: Arc<Cache>)
local_data_key!(pub current_location_key: Vec<String> )
thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default())
thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> =
RefCell::new(Vec::new()))
/// Generates the documentation for `crate` into the directory `dst`
pub fn run(mut krate: clean::Crate,
......@@ -280,10 +284,12 @@ pub fn run(mut krate: clean::Crate,
clean::NameValue(ref x, ref s)
if "html_playground_url" == x.as_slice() => {
cx.layout.playground_url = s.to_string();
let name = krate.name.clone();
if markdown::playground_krate.get().is_none() {
markdown::playground_krate.replace(Some(Some(name)));
}
markdown::PLAYGROUND_KRATE.with(|slot| {
if slot.borrow().is_none() {
let name = krate.name.clone();
*slot.borrow_mut() = Some(Some(name));
}
});
}
clean::Word(ref x)
if "html_no_source" == x.as_slice() => {
......@@ -297,7 +303,8 @@ pub fn run(mut krate: clean::Crate,
}
// Crawl the crate to build various caches used for the output
let analysis = ::analysiskey.get();
let analysis = ::ANALYSISKEY.with(|a| a.clone());
let analysis = analysis.borrow();
let public_items = analysis.as_ref().map(|a| a.public_items.clone());
let public_items = public_items.unwrap_or(NodeSet::new());
let paths: HashMap<ast::DefId, (Vec<String>, ItemType)> =
......@@ -370,8 +377,8 @@ pub fn run(mut krate: clean::Crate,
// Freeze the cache now that the index has been built. Put an Arc into TLS
// for future parallelization opportunities
let cache = Arc::new(cache);
cache_key.replace(Some(cache.clone()));
current_location_key.replace(Some(Vec::new()));
CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone());
CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear());
try!(write_shared(&cx, &krate, &*cache, index));
let krate = try!(render_sources(&mut cx, krate));
......@@ -1134,7 +1141,9 @@ fn render(w: io::File, cx: &Context, it: &clean::Item,
info!("Rendering an item to {}", w.path().display());
// A little unfortunate that this is done like this, but it sure
// does make formatting *a lot* nicer.
current_location_key.replace(Some(cx.current.clone()));
CURRENT_LOCATION_KEY.with(|slot| {
*slot.borrow_mut() = cx.current.clone();
});
let mut title = cx.current.connect("::");
if pushname {
......@@ -1177,7 +1186,7 @@ fn render(w: io::File, cx: &Context, it: &clean::Item,
&Item{ cx: cx, item: it }));
} else {
let mut url = "../".repeat(cx.current.len());
match cache_key.get().unwrap().paths.get(&it.def_id) {
match cache().paths.get(&it.def_id) {
Some(&(ref names, _)) => {
for name in names[..names.len() - 1].iter() {
url.push_str(name.as_slice());
......@@ -1324,7 +1333,7 @@ fn href(&self) -> Option<String> {
// If we don't know where the external documentation for this crate is
// located, then we return `None`.
} else {
let cache = cache_key.get().unwrap();
let cache = cache();
let path = &cache.external_paths[self.item.def_id];
let root = match cache.extern_locations[self.item.def_id.krate] {
Remote(ref s) => s.to_string(),
......@@ -1751,7 +1760,7 @@ fn trait_item(w: &mut fmt::Formatter, m: &clean::TraitMethod)
try!(write!(w, "</div>"));
}
let cache = cache_key.get().unwrap();
let cache = cache();
try!(write!(w, "
<h2 id='implementors'>Implementors</h2>
<ul class='item-list' id='implementors-list'>
......@@ -2013,7 +2022,7 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
}
fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
match cache_key.get().unwrap().impls.get(&it.def_id) {
match cache().impls.get(&it.def_id) {
Some(v) => {
let (non_trait, traits) = v.partitioned(|i| i.impl_.trait_.is_none());
if non_trait.len() > 0 {
......@@ -2101,7 +2110,7 @@ fn render_default_methods(w: &mut fmt::Formatter,
match i.impl_.trait_ {
Some(clean::ResolvedPath { did, .. }) => {
try!({
match cache_key.get().unwrap().traits.get(&did) {
match cache().traits.get(&did) {
Some(t) => try!(render_default_methods(w, t, &i.impl_)),
None => {}
}
......@@ -2220,3 +2229,7 @@ fn get_basic_keywords() -> &'static str {
fn make_item_keywords(it: &clean::Item) -> String {
format!("{}, {}", get_basic_keywords(), it.name.as_ref().unwrap())
}
pub fn cache() -> Arc<Cache> {
CACHE_KEY.with(|c| c.borrow().clone())
}
......@@ -28,12 +28,14 @@
extern crate "test" as testing;
#[phase(plugin, link)] extern crate log;
use std::io;
use std::io::File;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};
use serialize::{json, Decodable, Encodable};
use std::io::File;
use std::io;
use std::rc::Rc;
use externalfiles::ExternalHtml;
use serialize::{json, Decodable, Encodable};
// reexported from `clean` so it can be easily updated with the mod itself
pub use clean::SCHEMA_VERSION;
......@@ -84,7 +86,9 @@ pub mod html {
"unindent-comments",
];
local_data_key!(pub analysiskey: core::CrateAnalysis)
thread_local!(pub static ANALYSISKEY: Rc<RefCell<Option<core::CrateAnalysis>>> = {
Rc::new(RefCell::new(None))
})
struct Output {
krate: clean::Crate,
......@@ -338,7 +342,10 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche
core::run_core(libs, cfgs, externs, &cr, triple)
}).map_err(|_| "rustc failed").unwrap();
info!("finished with rustc");
analysiskey.replace(Some(analysis));
let mut analysis = Some(analysis);
ANALYSISKEY.with(|s| {
*s.borrow_mut() = analysis.take();
});
match matches.opt_str("crate-name") {
Some(name) => krate.name = name,
......
......@@ -55,7 +55,7 @@ pub fn render(input: &str, mut output: Path, matches: &getopts::Matches,
let input_str = load_or_return!(input, 1, 2);
let playground = matches.opt_str("markdown-playground-url");
if playground.is_some() {
markdown::playground_krate.replace(Some(None));
markdown::PLAYGROUND_KRATE.with(|s| { *s.borrow_mut() = None; });
}
let playground = playground.unwrap_or("".to_string());
......
......@@ -101,7 +101,9 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
// This stripper collects all *retained* nodes.
let mut retained = HashSet::new();
let analysis = super::analysiskey.get().unwrap();
let analysis = super::ANALYSISKEY.with(|a| a.clone());
let analysis = analysis.borrow();
let analysis = analysis.as_ref().unwrap();
let exported_items = analysis.exported_items.clone();
// strip all private items
......
......@@ -23,7 +23,7 @@
use clean::{ImplItem, Impl, Trait, TraitItem, TraitMethod, ProvidedMethod, RequiredMethod};
use clean::{TypeTraitItem, ViewItemItem, PrimitiveItem, Stability};
use html::render::cache_key;
use html::render::cache;
#[deriving(Zero, Encodable, Decodable, PartialEq, Eq)]
/// The counts for each stability level.
......@@ -116,7 +116,7 @@ fn count_stability(stab: Option<&Stability>) -> Counts {
}
fn summarize_methods(item: &Item) -> Counts {
match cache_key.get().unwrap().impls.get(&item.def_id) {
match cache().impls.get(&item.def_id) {
Some(v) => {
v.iter().map(|i| {
let count = count_stability(i.stability.as_ref());
......
......@@ -52,7 +52,6 @@
pub mod c_str;
pub mod exclusive;
pub mod local;
pub mod local_data;
pub mod mutex;
pub mod stack;
pub mod task;
......
此差异已折叠。
......@@ -27,7 +27,6 @@
use bookkeeping;
use mutex::NativeMutex;
use local_data;
use local::Local;
use thread::{mod, Thread};
use stack;
......@@ -40,7 +39,6 @@
/// This structure is currently undergoing major changes, and is
/// likely to be move/be merged with a `Thread` structure.
pub struct Task {
pub storage: LocalStorage,
pub unwinder: Unwinder,
pub death: Death,
pub name: Option<SendStr>,
......@@ -83,8 +81,6 @@ pub struct TaskOpts {
/// children tasks complete, recommend using a result future.
pub type Result = ::core::result::Result<(), Box<Any + Send>>;
pub struct LocalStorage(pub Option<local_data::Map>);
/// A handle to a blocked task. Usually this means having the Box<Task>
/// pointer by ownership, but if the task is killable, a killer can steal it
/// at any time.
......@@ -107,7 +103,6 @@ impl Task {
/// Creates a new uninitialized task.
pub fn new(stack_bounds: Option<(uint, uint)>, stack_guard: Option<uint>) -> Task {
Task {
storage: LocalStorage(None),
unwinder: Unwinder::new(),
death: Death::new(),
state: New,
......@@ -230,54 +225,11 @@ pub fn destroy(self: Box<Task>) -> Box<Task> {
/// This function consumes ownership of the task, deallocating it once it's
/// done being processed. It is assumed that TLD and the local heap have
/// already been destroyed and/or annihilated.
fn cleanup(self: Box<Task>, result: Result) -> Box<Task> {
// The first thing to do when cleaning up is to deallocate our local
// resources, such as TLD.
//
// FIXME: there are a number of problems with this code
//
// 1. If any TLD object fails destruction, then all of TLD will leak.
// This appears to be a consequence of #14875.
//
// 2. Setting a TLD key while destroying TLD will abort the runtime #14807.
//
// 3. The order of destruction of TLD matters, but either way is
// susceptible to leaks (see 2) #8302.
//
// That being said, there are a few upshots to this code
//
// 1. If TLD destruction fails, heap destruction will be attempted.
// There is a test for this at fail-during-tld-destroy.rs.
//
// 2. One failure in destruction is tolerable, so long as the task
// didn't originally panic while it was running.
//
// And with all that in mind, we attempt to clean things up!
let mut task = self.run(|| {
let mut task = Local::borrow(None::<Task>);
let tld = {
let &LocalStorage(ref mut optmap) = &mut task.storage;
optmap.take()
};
drop(task);
// First, destroy task-local storage. This may run user dtors.
drop(tld);
});
// If the above `run` block panicked, then it must be the case that the
// task had previously succeeded. This also means that the code below
// was recursively run via the `run` method invoking this method. In
// this case, we just make sure the world is as we thought, and return.
if task.is_destroyed() {
rtassert!(result.is_ok())
return task
}
fn cleanup(mut self: Box<Task>, result: Result) -> Box<Task> {
// After taking care of the data above, we need to transmit the result
// of this task.
let what_to_do = task.death.on_exit.take();
Local::put(task);
let what_to_do = self.death.on_exit.take();
Local::put(self);
// FIXME: this is running in a seriously constrained context. If this
// allocates TLD then it will likely abort the runtime. Similarly,
......@@ -549,16 +501,6 @@ mod test {
use std::task;
use unwind;
#[test]
fn tls() {
local_data_key!(key: String)
key.replace(Some("data".to_string()));
assert_eq!(key.get().unwrap().as_slice(), "data");
local_data_key!(key2: String)
key2.replace(Some("data".to_string()));
assert_eq!(key2.get().unwrap().as_slice(), "data");
}
#[test]
fn unwind() {
let result = task::try(proc()());
......
......@@ -1471,7 +1471,7 @@ fn test_insert() {
assert_eq!(*m.get(&2).unwrap(), 4);
}
local_data_key!(drop_vector: RefCell<Vec<int>>)
thread_local!(static DROP_VECTOR: RefCell<Vec<int>> = RefCell::new(Vec::new()))
#[deriving(Hash, PartialEq, Eq)]
struct Dropable {
......@@ -1480,8 +1480,9 @@ struct Dropable {
impl Dropable {
fn new(k: uint) -> Dropable {
let v = drop_vector.get().unwrap();
v.borrow_mut().as_mut_slice()[k] += 1;
DROP_VECTOR.with(|slot| {
slot.borrow_mut()[k] += 1;
});
Dropable { k: k }
}
......@@ -1489,8 +1490,9 @@ fn new(k: uint) -> Dropable {
impl Drop for Dropable {
fn drop(&mut self) {
let v = drop_vector.get().unwrap();
v.borrow_mut().as_mut_slice()[self.k] -= 1;
DROP_VECTOR.with(|slot| {
slot.borrow_mut()[self.k] -= 1;
});
}
}
......@@ -1502,16 +1504,18 @@ fn clone(&self) -> Dropable {
#[test]
fn test_drops() {
drop_vector.replace(Some(RefCell::new(Vec::from_elem(200, 0i))));
DROP_VECTOR.with(|slot| {
*slot.borrow_mut() = Vec::from_elem(200, 0i);
});
{
let mut m = HashMap::new();
let v = drop_vector.get().unwrap();
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
drop(v);
DROP_VECTOR.with(|v| {
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
});
for i in range(0u, 100) {
let d1 = Dropable::new(i);
......@@ -1519,11 +1523,11 @@ fn test_drops() {
m.insert(d1, d2);
}
let v = drop_vector.get().unwrap();
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 1);
}
drop(v);
DROP_VECTOR.with(|v| {
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 1);
}
});
for i in range(0u, 50) {
let k = Dropable::new(i);
......@@ -1531,41 +1535,46 @@ fn test_drops() {
assert!(v.is_some());
let v = drop_vector.get().unwrap();
assert_eq!(v.borrow().as_slice()[i], 1);
assert_eq!(v.borrow().as_slice()[i+100], 1);
DROP_VECTOR.with(|v| {
assert_eq!(v.borrow().as_slice()[i], 1);
assert_eq!(v.borrow().as_slice()[i+100], 1);
});
}
let v = drop_vector.get().unwrap();
for i in range(0u, 50) {
assert_eq!(v.borrow().as_slice()[i], 0);
assert_eq!(v.borrow().as_slice()[i+100], 0);
}
DROP_VECTOR.with(|v| {
for i in range(0u, 50) {
assert_eq!(v.borrow().as_slice()[i], 0);
assert_eq!(v.borrow().as_slice()[i+100], 0);
}
for i in range(50u, 100) {
assert_eq!(v.borrow().as_slice()[i], 1);
assert_eq!(v.borrow().as_slice()[i+100], 1);
}
for i in range(50u, 100) {
assert_eq!(v.borrow().as_slice()[i], 1);
assert_eq!(v.borrow().as_slice()[i+100], 1);
}
});
}
let v = drop_vector.get().unwrap();
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
DROP_VECTOR.with(|v| {
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
});
}
#[test]
fn test_move_iter_drops() {
drop_vector.replace(Some(RefCell::new(Vec::from_elem(200, 0i))));
DROP_VECTOR.with(|v| {
*v.borrow_mut() = Vec::from_elem(200, 0i);
});
let hm = {
let mut hm = HashMap::new();
let v = drop_vector.get().unwrap();
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
drop(v);
DROP_VECTOR.with(|v| {
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
});
for i in range(0u, 100) {
let d1 = Dropable::new(i);
......@@ -1573,11 +1582,11 @@ fn test_move_iter_drops() {
hm.insert(d1, d2);
}
let v = drop_vector.get().unwrap();
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 1);
}
drop(v);
DROP_VECTOR.with(|v| {
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 1);
}
});
hm
};
......@@ -1588,31 +1597,33 @@ fn test_move_iter_drops() {
{
let mut half = hm.into_iter().take(50);
let v = drop_vector.get().unwrap();
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 1);
}
drop(v);
DROP_VECTOR.with(|v| {
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 1);
}
});
for _ in half {}
let v = drop_vector.get().unwrap();
let nk = range(0u, 100).filter(|&i| {
v.borrow().as_slice()[i] == 1
}).count();
DROP_VECTOR.with(|v| {
let nk = range(0u, 100).filter(|&i| {
v.borrow().as_slice()[i] == 1
}).count();
let nv = range(0u, 100).filter(|&i| {
v.borrow().as_slice()[i+100] == 1
}).count();
let nv = range(0u, 100).filter(|&i| {
v.borrow().as_slice()[i+100] == 1
}).count();
assert_eq!(nk, 50);
assert_eq!(nv, 50);
assert_eq!(nk, 50);
assert_eq!(nv, 50);
});
};
let v = drop_vector.get().unwrap();
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
DROP_VECTOR.with(|v| {
for i in range(0u, 200) {
assert_eq!(v.borrow().as_slice()[i], 0);
}
});
}
#[test]
......
......@@ -12,10 +12,11 @@
use alloc::boxed::Box;
use any::{Any, AnyRefExt};
use cell::RefCell;
use fmt;
use io::{Writer, IoResult};
use kinds::Send;
use option::{Some, None};
use option::{Some, None, Option};
use result::Ok;
use rt::backtrace;
use rustrt::{Stderr, Stdio};
......@@ -25,7 +26,9 @@
use string::String;
// Defined in this module instead of io::stdio so that the unwinding
local_data_key!(pub local_stderr: Box<Writer + Send>)
thread_local!(pub static LOCAL_STDERR: RefCell<Option<Box<Writer + Send>>> = {
RefCell::new(None)
})
impl Writer for Stdio {
fn write(&mut self, bytes: &[u8]) -> IoResult<()> {
......@@ -74,7 +77,8 @@ pub fn on_fail(obj: &Any + Send, file: &'static str, line: uint) {
{
let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
match local_stderr.replace(None) {
let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
match prev {
Some(mut stderr) => {
// FIXME: what to do when the task printing panics?
let _ = writeln!(stderr,
......@@ -83,7 +87,10 @@ pub fn on_fail(obj: &Any + Send, file: &'static str, line: uint) {
if backtrace::log_enabled() {
let _ = backtrace::write(&mut *stderr);
}
local_stderr.replace(Some(stderr));
let mut s = Some(stderr);
LOCAL_STDERR.with(|slot| {
*slot.borrow_mut() = s.take();
});
}
None => {
let _ = writeln!(&mut err, "task '{}' panicked at '{}', {}:{}",
......
......@@ -29,22 +29,24 @@
use self::StdSource::*;
use failure::local_stderr;
use boxed::Box;
use cell::RefCell;
use failure::LOCAL_STDERR;
use fmt;
use io::{Reader, Writer, IoResult, IoError, OtherIoError,
standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
use iter::Iterator;
use kinds::Send;
use libc;
use mem;
use option::{Option, Some, None};
use boxed::Box;
use sys::{fs, tty};
use result::{Ok, Err};
use rustrt;
use rustrt::local::Local;
use rustrt::task::Task;
use slice::SlicePrelude;
use str::StrPrelude;
use sys::{fs, tty};
use uint;
// And so begins the tale of acquiring a uv handle to a stdio stream on all
......@@ -87,7 +89,9 @@ fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T {
}
}
local_data_key!(local_stdout: Box<Writer + Send>)
thread_local!(static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = {
RefCell::new(None)
})
/// Creates a new non-blocking handle to the stdin of the current process.
///
......@@ -167,7 +171,10 @@ pub fn stderr_raw() -> StdWriter {
/// Note that this does not need to be called for all new tasks; the default
/// output handle is to the process's stdout stream.
pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
local_stdout.replace(Some(stdout)).and_then(|mut s| {
let mut new = Some(stdout);
LOCAL_STDOUT.with(|slot| {
mem::replace(&mut *slot.borrow_mut(), new.take())
}).and_then(|mut s| {
let _ = s.flush();
Some(s)
})
......@@ -182,7 +189,10 @@ pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
/// Note that this does not need to be called for all new tasks; the default
/// output handle is to the process's stderr stream.
pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
local_stderr.replace(Some(stderr)).and_then(|mut s| {
let mut new = Some(stderr);
LOCAL_STDERR.with(|slot| {
mem::replace(&mut *slot.borrow_mut(), new.take())
}).and_then(|mut s| {
let _ = s.flush();
Some(s)
})
......@@ -200,11 +210,16 @@ pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
// })
fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) {
let result = if Local::exists(None::<Task>) {
let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| {
let mut my_stdout = LOCAL_STDOUT.with(|slot| {
slot.borrow_mut().take()
}).unwrap_or_else(|| {
box stdout() as Box<Writer + Send>
});
let result = f(&mut *my_stdout);
local_stdout.replace(Some(my_stdout));
let mut var = Some(my_stdout);
LOCAL_STDOUT.with(|slot| {
*slot.borrow_mut() = var.take();
});
result
} else {
let mut io = rustrt::Stdout;
......
......@@ -170,7 +170,6 @@
pub use core_collections::vec;
pub use rustrt::c_str;
pub use rustrt::local_data;
pub use unicode::char;
......@@ -209,17 +208,25 @@
#[path = "num/f32.rs"] pub mod f32;
#[path = "num/f64.rs"] pub mod f64;
pub mod rand;
pub mod ascii;
pub mod time;
/* Common traits */
pub mod error;
pub mod num;
/* Runtime and platform support */
pub mod thread_local;
pub mod c_vec;
pub mod dynamic_lib;
pub mod fmt;
pub mod io;
pub mod os;
pub mod path;
pub mod rand;
pub mod time;
/* Common data structures */
pub mod collections;
......@@ -230,15 +237,6 @@
pub mod task;
pub mod sync;
/* Runtime and platform support */
pub mod c_vec;
pub mod dynamic_lib;
pub mod os;
pub mod io;
pub mod path;
pub mod fmt;
#[cfg(unix)]
#[path = "sys/unix/mod.rs"] mod sys;
#[cfg(windows)]
......@@ -263,10 +261,12 @@ mod std {
pub use error; // used for try!()
pub use fmt; // used for any formatting strings
pub use io; // used for println!()
pub use local_data; // used for local_data_key!()
pub use option; // used for bitflags!{}
pub use rt; // used for panic!()
pub use vec; // used for vec![]
pub use cell; // used for tls!
pub use thread_local; // used for thread_local!
pub use kinds; // used for tls!
// The test runner calls ::std::os::args() but really wants realstd
#[cfg(test)] pub use realstd::os as os;
......@@ -276,4 +276,5 @@ mod std {
pub use slice;
pub use boxed; // used for vec![]
}
......@@ -304,28 +304,6 @@ fn _run_fmt(fmt: &::std::fmt::Arguments) -> ! {
($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*))
)
/// Declare a task-local key with a specific type.
///
/// # Example
///
/// ```
/// local_data_key!(my_integer: int)
///
/// my_integer.replace(Some(2));
/// println!("{}", my_integer.get().map(|a| *a));
/// ```
#[macro_export]
macro_rules! local_data_key(
($name:ident: $ty:ty) => (
#[allow(non_upper_case_globals)]
static $name: ::std::local_data::Key<$ty> = &::std::local_data::KeyValueKey;
);
(pub $name:ident: $ty:ty) => (
#[allow(non_upper_case_globals)]
pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::KeyValueKey;
);
)
/// Helper macro for unwrapping `Result` values while returning early with an
/// error if the value of the expression is `Err`. For more information, see
/// `std::io`.
......
......@@ -226,7 +226,6 @@
use io::IoResult;
use iter::Iterator;
use mem;
use option::{Some, None};
use rc::Rc;
use result::{Ok, Err};
use vec::Vec;
......@@ -337,24 +336,18 @@ pub struct TaskRng {
/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`.
pub fn task_rng() -> TaskRng {
// used to make space in TLS for a random number generator
local_data_key!(TASK_RNG_KEY: Rc<RefCell<TaskRngInner>>)
match TASK_RNG_KEY.get() {
None => {
let r = match StdRng::new() {
Ok(r) => r,
Err(e) => panic!("could not initialize task_rng: {}", e)
};
let rng = reseeding::ReseedingRng::new(r,
TASK_RNG_RESEED_THRESHOLD,
TaskRngReseeder);
let rng = Rc::new(RefCell::new(rng));
TASK_RNG_KEY.replace(Some(rng.clone()));
TaskRng { rng: rng }
}
Some(rng) => TaskRng { rng: rng.clone() }
}
thread_local!(static TASK_RNG_KEY: Rc<RefCell<TaskRngInner>> = {
let r = match StdRng::new() {
Ok(r) => r,
Err(e) => panic!("could not initialize task_rng: {}", e)
};
let rng = reseeding::ReseedingRng::new(r,
TASK_RNG_RESEED_THRESHOLD,
TaskRngReseeder);
Rc::new(RefCell::new(rng))
})
TaskRng { rng: TASK_RNG_KEY.with(|t| t.clone()) }
}
impl Rng for TaskRng {
......
......@@ -210,28 +210,4 @@ fn test_sendable_future() {
});
assert_eq!(rx.recv(), expected);
}
#[test]
fn test_dropped_future_doesnt_panic() {
struct Bomb(Sender<bool>);
local_data_key!(LOCAL: Bomb)
impl Drop for Bomb {
fn drop(&mut self) {
let Bomb(ref tx) = *self;
tx.send(task::failing());
}
}
// Spawn a future, but drop it immediately. When we receive the result
// later on, we should never view the task as having panicked.
let (tx, rx) = channel();
drop(Future::spawn(proc() {
LOCAL.replace(Some(Bomb(tx)));
}));
// Make sure the future didn't panic the task.
assert!(!rx.recv());
}
}
......@@ -21,6 +21,7 @@
pub mod net;
pub mod helper_thread;
pub mod thread_local;
// common error constructors
......
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! OS-based thread local storage
//!
//! This module provides an implementation of OS-based thread local storage,
//! using the native OS-provided facilities (think `TlsAlloc` or
//! `pthread_setspecific`). The interface of this differs from the other types
//! of thread-local-storage provided in this crate in that OS-based TLS can only
//! get/set pointers,
//!
//! This module also provides two flavors of TLS. One is intended for static
//! initialization, and does not contain a `Drop` implementation to deallocate
//! the OS-TLS key. The other is a type which does implement `Drop` and hence
//! has a safe interface.
//!
//! # Usage
//!
//! This module should likely not be used directly unless other primitives are
//! being built on. types such as `thread_local::scoped::Key` are likely much
//! more useful in practice than this OS-based version which likely requires
//! unsafe code to interoperate with.
//!
//! # Example
//!
//! Using a dynamically allocated TLS key. Note that this key can be shared
//! among many threads via an `Arc`.
//!
//! ```rust,ignore
//! let key = Key::new(None);
//! assert!(key.get().is_null());
//! key.set(1 as *mut u8);
//! assert!(!key.get().is_null());
//!
//! drop(key); // deallocate this TLS slot.
//! ```
//!
//! Sometimes a statically allocated key is either required or easier to work
//! with, however.
//!
//! ```rust,ignore
//! static KEY: StaticKey = INIT;
//!
//! unsafe {
//! assert!(KEY.get().is_null());
//! KEY.set(1 as *mut u8);
//! }
//! ```
#![allow(non_camel_case_types)]
use prelude::*;
use kinds::marker;
use mem;
use rustrt::exclusive::Exclusive;
use rustrt;
use sync::atomic::{mod, AtomicUint};
use sync::{Once, ONCE_INIT};
use sys::thread_local as imp;
/// A type for TLS keys that are statically allocated.
///
/// This type is entirely `unsafe` to use as it does not protect against
/// use-after-deallocation or use-during-deallocation.
///
/// The actual OS-TLS key is lazily allocated when this is used for the first
/// time. The key is also deallocated when the Rust runtime exits or `destroy`
/// is called, whichever comes first.
///
/// # Example
///
/// ```ignore
/// use tls::os::{StaticKey, INIT};
///
/// static KEY: StaticKey = INIT;
///
/// unsafe {
/// assert!(KEY.get().is_null());
/// KEY.set(1 as *mut u8);
/// }
/// ```
pub struct StaticKey {
/// Inner static TLS key (internals), created with by `INIT_INNER` in this
/// module.
pub inner: StaticKeyInner,
/// Destructor for the TLS value.
///
/// See `Key::new` for information about when the destructor runs and how
/// it runs.
pub dtor: Option<unsafe extern fn(*mut u8)>,
}
/// Inner contents of `StaticKey`, created by the `INIT_INNER` constant.
pub struct StaticKeyInner {
key: AtomicUint,
nc: marker::NoCopy,
}
/// A type for a safely managed OS-based TLS slot.
///
/// This type allocates an OS TLS key when it is initialized and will deallocate
/// the key when it falls out of scope. When compared with `StaticKey`, this
/// type is entirely safe to use.
///
/// Implementations will likely, however, contain unsafe code as this type only
/// operates on `*mut u8`, an unsafe pointer.
///
/// # Example
///
/// ```rust,ignore
/// use tls::os::Key;
///
/// let key = Key::new(None);
/// assert!(key.get().is_null());
/// key.set(1 as *mut u8);
/// assert!(!key.get().is_null());
///
/// drop(key); // deallocate this TLS slot.
/// ```
pub struct Key {
key: imp::Key,
}
/// Constant initialization value for static TLS keys.
///
/// This value specifies no destructor by default.
pub const INIT: StaticKey = StaticKey {
inner: INIT_INNER,
dtor: None,
};
/// Constant initialization value for the inner part of static TLS keys.
///
/// This value allows specific configuration of the destructor for a TLS key.
pub const INIT_INNER: StaticKeyInner = StaticKeyInner {
key: atomic::INIT_ATOMIC_UINT,
nc: marker::NoCopy,
};
static INIT_KEYS: Once = ONCE_INIT;
static mut KEYS: *mut Exclusive<Vec<imp::Key>> = 0 as *mut _;
impl StaticKey {
/// Gets the value associated with this TLS key
///
/// This will lazily allocate a TLS key from the OS if one has not already
/// been allocated.
#[inline]
pub unsafe fn get(&self) -> *mut u8 { imp::get(self.key()) }
/// Sets this TLS key to a new value.
///
/// This will lazily allocate a TLS key from the OS if one has not already
/// been allocated.
#[inline]
pub unsafe fn set(&self, val: *mut u8) { imp::set(self.key(), val) }
/// Deallocates this OS TLS key.
///
/// This function is unsafe as there is no guarantee that the key is not
/// currently in use by other threads or will not ever be used again.
///
/// Note that this does *not* run the user-provided destructor if one was
/// specified at definition time. Doing so must be done manually.
pub unsafe fn destroy(&self) {
match self.inner.key.swap(0, atomic::SeqCst) {
0 => {}
n => { unregister_key(n as imp::Key); imp::destroy(n as imp::Key) }
}
}
#[inline]
unsafe fn key(&self) -> imp::Key {
match self.inner.key.load(atomic::Relaxed) {
0 => self.lazy_init() as imp::Key,
n => n as imp::Key
}
}
unsafe fn lazy_init(&self) -> uint {
let key = imp::create(self.dtor);
assert!(key != 0);
match self.inner.key.compare_and_swap(0, key as uint, atomic::SeqCst) {
// The CAS succeeded, so we've created the actual key
0 => {
register_key(key);
key as uint
}
// If someone beat us to the punch, use their key instead
n => { imp::destroy(key); n }
}
}
}
impl Key {
/// Create a new managed OS TLS key.
///
/// This key will be deallocated when the key falls out of scope.
///
/// The argument provided is an optionally-specified destructor for the
/// value of this TLS key. When a thread exits and the value for this key
/// is non-null the destructor will be invoked. The TLS value will be reset
/// to null before the destructor is invoked.
///
/// Note that the destructor will not be run when the `Key` goes out of
/// scope.
#[inline]
pub fn new(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
Key { key: unsafe { imp::create(dtor) } }
}
/// See StaticKey::get
#[inline]
pub fn get(&self) -> *mut u8 {
unsafe { imp::get(self.key) }
}
/// See StaticKey::set
#[inline]
pub fn set(&self, val: *mut u8) {
unsafe { imp::set(self.key, val) }
}
}
impl Drop for Key {
fn drop(&mut self) {
unsafe { imp::destroy(self.key) }
}
}
fn init_keys() {
let keys = box Exclusive::new(Vec::<imp::Key>::new());
unsafe {
KEYS = mem::transmute(keys);
}
rustrt::at_exit(proc() unsafe {
let keys: Box<Exclusive<Vec<imp::Key>>> = mem::transmute(KEYS);
KEYS = 0 as *mut _;
let keys = keys.lock();
for key in keys.iter() {
imp::destroy(*key);
}
});
}
fn register_key(key: imp::Key) {
INIT_KEYS.doit(init_keys);
let mut keys = unsafe { (*KEYS).lock() };
keys.push(key);
}
fn unregister_key(key: imp::Key) {
INIT_KEYS.doit(init_keys);
let mut keys = unsafe { (*KEYS).lock() };
keys.retain(|k| *k != key);
}
#[cfg(test)]
mod tests {
use prelude::*;
use super::{Key, StaticKey, INIT_INNER};
fn assert_sync<T: Sync>() {}
fn assert_send<T: Send>() {}
#[test]
fn smoke() {
assert_sync::<Key>();
assert_send::<Key>();
let k1 = Key::new(None);
let k2 = Key::new(None);
assert!(k1.get().is_null());
assert!(k2.get().is_null());
k1.set(1 as *mut _);
k2.set(2 as *mut _);
assert_eq!(k1.get() as uint, 1);
assert_eq!(k2.get() as uint, 2);
}
#[test]
fn statik() {
static K1: StaticKey = StaticKey { inner: INIT_INNER, dtor: None };
static K2: StaticKey = StaticKey { inner: INIT_INNER, dtor: None };
unsafe {
assert!(K1.get().is_null());
assert!(K2.get().is_null());
K1.set(1 as *mut _);
K2.set(2 as *mut _);
assert_eq!(K1.get() as uint, 1);
assert_eq!(K2.get() as uint, 2);
}
}
}
......@@ -34,14 +34,15 @@
pub mod c;
pub mod fs;
pub mod helper_signal;
pub mod os;
pub mod tcp;
pub mod udp;
pub mod pipe;
pub mod helper_signal;
pub mod process;
pub mod tcp;
pub mod timer;
pub mod thread_local;
pub mod tty;
pub mod udp;
pub mod addrinfo {
pub use sys_common::net::get_host_addresses;
......
......@@ -8,26 +8,45 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::task;
use prelude::*;
use libc::c_int;
static mut DROPS: uint = 0;
pub type Key = pthread_key_t;
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
unsafe { DROPS += 1; }
panic!()
}
#[inline]
pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
let mut key = 0;
assert_eq!(pthread_key_create(&mut key, dtor), 0);
return key;
}
fn main() {
let _ = task::try(proc() {
local_data_key!(foo: Foo);
foo.replace(Some(Foo));
});
#[inline]
pub unsafe fn set(key: Key, value: *mut u8) {
let r = pthread_setspecific(key, value);
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn get(key: Key) -> *mut u8 {
pthread_getspecific(key)
}
unsafe {
assert_eq!(DROPS, 1);
}
#[inline]
pub unsafe fn destroy(key: Key) {
let r = pthread_key_delete(key);
debug_assert_eq!(r, 0);
}
#[cfg(target_os = "macos")]
type pthread_key_t = ::libc::c_ulong;
#[cfg(not(target_os = "macos"))]
type pthread_key_t = ::libc::c_uint;
extern {
fn pthread_key_create(key: *mut pthread_key_t,
dtor: Option<unsafe extern fn(*mut u8)>) -> c_int;
fn pthread_key_delete(key: pthread_key_t) -> c_int;
fn pthread_getspecific(key: pthread_key_t) -> *mut u8;
fn pthread_setspecific(key: pthread_key_t, value: *mut u8) -> c_int;
}
......@@ -35,14 +35,15 @@
pub mod c;
pub mod fs;
pub mod helper_signal;
pub mod os;
pub mod tcp;
pub mod udp;
pub mod pipe;
pub mod helper_signal;
pub mod process;
pub mod tcp;
pub mod thread_local;
pub mod timer;
pub mod tty;
pub mod udp;
pub mod addrinfo {
pub use sys_common::net::get_host_addresses;
......
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use prelude::*;
use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
use mem;
use rustrt;
use rustrt::exclusive::Exclusive;
use sync::{ONCE_INIT, Once};
pub type Key = DWORD;
pub type Dtor = unsafe extern fn(*mut u8);
// Turns out, like pretty much everything, Windows is pretty close the
// functionality that Unix provides, but slightly different! In the case of
// TLS, Windows does not provide an API to provide a destructor for a TLS
// variable. This ends up being pretty crucial to this implementation, so we
// need a way around this.
//
// The solution here ended up being a little obscure, but fear not, the
// internet has informed me [1][2] that this solution is not unique (no way
// I could have thought of it as well!). The key idea is to insert some hook
// somewhere to run arbitrary code on thread termination. With this in place
// we'll be able to run anything we like, including all TLS destructors!
//
// To accomplish this feat, we perform a number of tasks, all contained
// within this module:
//
// * All TLS destructors are tracked by *us*, not the windows runtime. This
// means that we have a global list of destructors for each TLS key that
// we know about.
// * When a TLS key is destroyed, we're sure to remove it from the dtor list
// if it's in there.
// * When a thread exits, we run over the entire list and run dtors for all
// non-null keys. This attempts to match Unix semantics in this regard.
//
// This ends up having the overhead of using a global list, having some
// locks here and there, and in general just adding some more code bloat. We
// attempt to optimize runtime by forgetting keys that don't have
// destructors, but this only gets us so far.
//
// For more details and nitty-gritty, see the code sections below!
//
// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base
// /threading/thread_local_storage_win.cc#L42
static INIT_DTORS: Once = ONCE_INIT;
static mut DTORS: *mut Exclusive<Vec<(Key, Dtor)>> = 0 as *mut _;
// -------------------------------------------------------------------------
// Native bindings
//
// This section is just raw bindings to the native functions that Windows
// provides, There's a few extra calls to deal with destructors.
#[inline]
pub unsafe fn create(dtor: Option<Dtor>) -> Key {
const TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF;
let key = TlsAlloc();
assert!(key != TLS_OUT_OF_INDEXES);
match dtor {
Some(f) => register_dtor(key, f),
None => {}
}
return key;
}
#[inline]
pub unsafe fn set(key: Key, value: *mut u8) {
let r = TlsSetValue(key, value as LPVOID);
debug_assert!(r != 0);
}
#[inline]
pub unsafe fn get(key: Key) -> *mut u8 {
TlsGetValue(key) as *mut u8
}
#[inline]
pub unsafe fn destroy(key: Key) {
if unregister_dtor(key) {
// FIXME: Currently if a key has a destructor associated with it we
// can't actually ever unregister it. If we were to
// unregister it, then any key destruction would have to be
// serialized with respect to actually running destructors.
//
// We want to avoid a race where right before run_dtors runs
// some destructors TlsFree is called. Allowing the call to
// TlsFree would imply that the caller understands that *all
// known threads* are not exiting, which is quite a difficult
// thing to know!
//
// For now we just leak all keys with dtors to "fix" this.
// Note that source [2] above shows precedent for this sort
// of strategy.
} else {
let r = TlsFree(key);
debug_assert!(r != 0);
}
}
extern "system" {
fn TlsAlloc() -> DWORD;
fn TlsFree(dwTlsIndex: DWORD) -> BOOL;
fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
}
// -------------------------------------------------------------------------
// Dtor registration
//
// These functions are associated with registering and unregistering
// destructors. They're pretty simple, they just push onto a vector and scan
// a vector currently.
//
// FIXME: This could probably be at least a little faster with a BTree.
fn init_dtors() {
let dtors = box Exclusive::new(Vec::<(Key, Dtor)>::new());
unsafe {
DTORS = mem::transmute(dtors);
}
rustrt::at_exit(proc() unsafe {
mem::transmute::<_, Box<Exclusive<Vec<(Key, Dtor)>>>>(DTORS);
DTORS = 0 as *mut _;
});
}
unsafe fn register_dtor(key: Key, dtor: Dtor) {
INIT_DTORS.doit(init_dtors);
let mut dtors = (*DTORS).lock();
dtors.push((key, dtor));
}
unsafe fn unregister_dtor(key: Key) -> bool {
if DTORS.is_null() { return false }
let mut dtors = (*DTORS).lock();
let before = dtors.len();
dtors.retain(|&(k, _)| k != key);
dtors.len() != before
}
// -------------------------------------------------------------------------
// Where the Magic (TM) Happens
//
// If you're looking at this code, and wondering "what is this doing?",
// you're not alone! I'll try to break this down step by step:
//
// # What's up with CRT$XLB?
//
// For anything about TLS destructors to work on Windows, we have to be able
// to run *something* when a thread exits. To do so, we place a very special
// static in a very special location. If this is encoded in just the right
// way, the kernel's loader is apparently nice enough to run some function
// of ours whenever a thread exits! How nice of the kernel!
//
// Lots of detailed information can be found in source [1] above, but the
// gist of it is that this is leveraging a feature of Microsoft's PE format
// (executable format) which is not actually used by any compilers today.
// This apparently translates to any callbacks in the ".CRT$XLB" section
// being run on certain events.
//
// So after all that, we use the compiler's #[link_section] feature to place
// a callback pointer into the magic section so it ends up being called.
//
// # What's up with this callback?
//
// The callback specified receives a number of parameters from... someone!
// (the kernel? the runtime? I'm not qute sure!) There are a few events that
// this gets invoked for, but we're currentl only interested on when a
// thread or a process "detaches" (exits). The process part happens for the
// last thread and the thread part happens for any normal thread.
//
// # Ok, what's up with running all these destructors?
//
// This will likely need to be improved over time, but this function
// attempts a "poor man's" destructor callback system. To do this we clone a
// local copy of the dtor list to start out with. This is our fudgy attempt
// to not hold the lock while destructors run and not worry about the list
// changing while we're looking at it.
//
// Once we've got a list of what to run, we iterate over all keys, check
// their values, and then run destructors if the values turn out to be non
// null (setting them to null just beforehand). We do this a few times in a
// loop to basically match Unix semantics. If we don't reach a fixed point
// after a short while then we just inevitably leak something most likely.
//
// # The article mentions crazy stuff about "/INCLUDE"?
//
// It sure does! This seems to work for now, so maybe we'll just run into
// that if we start linking with msvc?
#[link_section = ".CRT$XLB"]
#[linkage = "external"]
#[allow(warnings)]
pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD,
LPVOID) =
on_tls_callback;
#[allow(warnings)]
unsafe extern "system" fn on_tls_callback(h: LPVOID,
dwReason: DWORD,
pv: LPVOID) {
const DLL_THREAD_DETACH: DWORD = 3;
const DLL_PROCESS_DETACH: DWORD = 0;
if dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH {
run_dtors();
}
}
unsafe fn run_dtors() {
if DTORS.is_null() { return }
let mut any_run = true;
for _ in range(0, 5i) {
if !any_run { break }
any_run = false;
let dtors = (*DTORS).lock().iter().map(|p| *p).collect::<Vec<_>>();
for &(key, dtor) in dtors.iter() {
let ptr = TlsGetValue(key);
if !ptr.is_null() {
TlsSetValue(key, 0 as *mut _);
dtor(ptr as *mut _);
any_run = true;
}
}
}
}
此差异已折叠。
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Scoped thread-local storage
//!
//! This module provides the ability to generate *scoped* thread-local
//! variables. In this sense, scoped indicates that thread local storage
//! actually stores a reference to a value, and this reference is only placed
//! in storage for a scoped amount of time.
//!
//! There are no restrictions on what types can be placed into a scoped
//! variable, but all scoped variables are initialized to the equivalent of
//! null. Scoped thread local stor is useful when a value is present for a known
//! period of time and it is not required to relinquish ownership of the
//! contents.
//!
//! # Example
//!
//! ```
//! scoped_thread_local!(static FOO: uint)
//!
//! // Initially each scoped slot is empty.
//! assert!(!FOO.is_set());
//!
//! // When inserting a value, the value is only in place for the duration
//! // of the closure specified.
//! FOO.set(&1, || {
//! FOO.with(|slot| {
//! assert_eq!(*slot, 1);
//! });
//! });
//! ```
#![macro_escape]
use prelude::*;
// macro hygiene sure would be nice, wouldn't it?
#[doc(hidden)] pub use self::imp::KeyInner;
#[doc(hidden)] pub use sys_common::thread_local::INIT as OS_INIT;
/// Type representing a thread local storage key corresponding to a reference
/// to the type parameter `T`.
///
/// Keys are statically allocated and can contain a reference to an instance of
/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
/// and `with`, both of which currently use closures to control the scope of
/// their contents.
pub struct Key<T> { #[doc(hidden)] pub inner: KeyInner<T> }
/// Declare a new scoped thread local storage key.
///
/// This macro declares a `static` item on which methods are used to get and
/// set the value stored within.
#[macro_export]
macro_rules! scoped_thread_local(
(static $name:ident: $t:ty) => (
__scoped_thread_local_inner!(static $name: $t)
);
(pub static $name:ident: $t:ty) => (
__scoped_thread_local_inner!(pub static $name: $t)
);
)
#[macro_export]
#[doc(hidden)]
macro_rules! __scoped_thread_local_inner(
(static $name:ident: $t:ty) => (
#[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")),
thread_local)]
static $name: ::std::thread_local::scoped::Key<$t> =
__scoped_thread_local_inner!($t);
);
(pub static $name:ident: $t:ty) => (
#[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")),
thread_local)]
pub static $name: ::std::thread_local::scoped::Key<$t> =
__scoped_thread_local_inner!($t);
);
($t:ty) => ({
use std::thread_local::scoped::Key as __Key;
#[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
const INIT: __Key<$t> = __Key {
inner: ::std::thread_local::scoped::KeyInner {
inner: ::std::cell::UnsafeCell { value: 0 as *mut _ },
}
};
#[cfg(any(windows, target_os = "android", target_os = "ios"))]
const INIT: __Key<$t> = __Key {
inner: ::std::thread_local::scoped::KeyInner {
inner: ::std::thread_local::scoped::OS_INIT,
marker: ::std::kinds::marker::InvariantType,
}
};
INIT
})
)
impl<T> Key<T> {
/// Insert a value into this scoped thread local storage slot for a
/// duration of a closure.
///
/// While `cb` is running, the value `t` will be returned by `get` unless
/// this function is called recursively inside of `cb`.
///
/// Upon return, this function will restore the previous value, if any
/// was available.
///
/// # Example
///
/// ```
/// scoped_thread_local!(static FOO: uint)
///
/// FOO.set(&100, || {
/// let val = FOO.with(|v| *v);
/// assert_eq!(val, 100);
///
/// // set can be called recursively
/// FOO.set(&101, || {
/// // ...
/// });
///
/// // Recursive calls restore the previous value.
/// let val = FOO.with(|v| *v);
/// assert_eq!(val, 100);
/// });
/// ```
pub fn set<R>(&'static self, t: &T, cb: || -> R) -> R {
struct Reset<'a, T: 'a> {
key: &'a KeyInner<T>,
val: *mut T,
}
#[unsafe_destructor]
impl<'a, T> Drop for Reset<'a, T> {
fn drop(&mut self) {
unsafe { self.key.set(self.val) }
}
}
let prev = unsafe {
let prev = self.inner.get();
self.inner.set(t as *const T as *mut T);
prev
};
let _reset = Reset { key: &self.inner, val: prev };
cb()
}
/// Get a value out of this scoped variable.
///
/// This function takes a closure which receives the value of this
/// variable.
///
/// # Panics
///
/// This function will panic if `set` has not previously been called.
///
/// # Example
///
/// ```no_run
/// scoped_thread_local!(static FOO: uint)
///
/// FOO.with(|slot| {
/// // work with `slot`
/// });
/// ```
pub fn with<R>(&'static self, cb: |&T| -> R) -> R {
unsafe {
let ptr = self.inner.get();
assert!(!ptr.is_null(), "cannot access a scoped thread local \
variable without calling `set` first");
cb(&*ptr)
}
}
/// Test whether this TLS key has been `set` for the current thread.
pub fn is_set(&'static self) -> bool {
unsafe { !self.inner.get().is_null() }
}
}
#[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
mod imp {
use std::cell::UnsafeCell;
// FIXME: Should be a `Cell`, but that's not `Sync`
#[doc(hidden)]
pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> }
#[doc(hidden)]
impl<T> KeyInner<T> {
#[doc(hidden)]
pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; }
#[doc(hidden)]
pub unsafe fn get(&self) -> *mut T { *self.inner.get() }
}
}
#[cfg(any(windows, target_os = "android", target_os = "ios"))]
mod imp {
use kinds::marker;
use sys_common::thread_local::StaticKey as OsStaticKey;
#[doc(hidden)]
pub struct KeyInner<T> {
pub inner: OsStaticKey,
pub marker: marker::InvariantType<T>,
}
#[doc(hidden)]
impl<T> KeyInner<T> {
#[doc(hidden)]
pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) }
#[doc(hidden)]
pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ }
}
}
#[cfg(test)]
mod tests {
use cell::Cell;
use prelude::*;
#[test]
fn smoke() {
scoped_thread_local!(static BAR: uint)
assert!(!BAR.is_set());
BAR.set(&1, || {
assert!(BAR.is_set());
BAR.with(|slot| {
assert_eq!(*slot, 1);
});
});
assert!(!BAR.is_set());
}
#[test]
fn cell_allowed() {
scoped_thread_local!(static BAR: Cell<uint>)
BAR.set(&Cell::new(1), || {
BAR.with(|slot| {
assert_eq!(slot.get(), 1);
});
});
}
}
......@@ -25,21 +25,20 @@
use parse::token;
use ptr::P;
use std::collections::HashSet;
use std::cell::{RefCell, Cell};
use std::collections::BitvSet;
use std::collections::HashSet;
local_data_key!(used_attrs: BitvSet)
thread_local!(static USED_ATTRS: RefCell<BitvSet> = RefCell::new(BitvSet::new()))
pub fn mark_used(attr: &Attribute) {
let mut used = used_attrs.replace(None).unwrap_or_else(|| BitvSet::new());
let AttrId(id) = attr.node.id;
used.insert(id);
used_attrs.replace(Some(used));
USED_ATTRS.with(|slot| slot.borrow_mut().insert(id));
}
pub fn is_used(attr: &Attribute) -> bool {
let AttrId(id) = attr.node.id;
used_attrs.get().map_or(false, |used| used.contains(&id))
USED_ATTRS.with(|slot| slot.borrow().contains(&id))
}
pub trait AttrMetaMethods {
......@@ -167,11 +166,14 @@ pub fn mk_word_item(name: InternedString) -> P<MetaItem> {
P(dummy_spanned(MetaWord(name)))
}
local_data_key!(next_attr_id: uint)
thread_local!(static NEXT_ATTR_ID: Cell<uint> = Cell::new(0))
pub fn mk_attr_id() -> AttrId {
let id = next_attr_id.replace(None).unwrap_or(0);
next_attr_id.replace(Some(id + 1));
let id = NEXT_ATTR_ID.with(|slot| {
let r = slot.get();
slot.set(r + 1);
r
});
AttrId(id)
}
......
......@@ -18,31 +18,23 @@
use parse::token;
use ptr::P;
local_data_key!(registered_diagnostics: RefCell<HashMap<Name, Option<Name>>>)
local_data_key!(used_diagnostics: RefCell<HashMap<Name, Span>>)
thread_local!(static REGISTERED_DIAGNOSTICS: RefCell<HashMap<Name, Option<Name>>> = {
RefCell::new(HashMap::new())
})
thread_local!(static USED_DIAGNOSTICS: RefCell<HashMap<Name, Span>> = {
RefCell::new(HashMap::new())
})
fn with_registered_diagnostics<T>(f: |&mut HashMap<Name, Option<Name>>| -> T) -> T {
match registered_diagnostics.get() {
Some(cell) => f(cell.borrow_mut().deref_mut()),
None => {
let mut map = HashMap::new();
let value = f(&mut map);
registered_diagnostics.replace(Some(RefCell::new(map)));
value
}
}
REGISTERED_DIAGNOSTICS.with(|slot| {
f(&mut *slot.borrow_mut())
})
}
fn with_used_diagnostics<T>(f: |&mut HashMap<Name, Span>| -> T) -> T {
match used_diagnostics.get() {
Some(cell) => f(cell.borrow_mut().deref_mut()),
None => {
let mut map = HashMap::new();
let value = f(&mut map);
used_diagnostics.replace(Some(RefCell::new(map)));
value
}
}
USED_DIAGNOSTICS.with(|slot| {
f(&mut *slot.borrow_mut())
})
}
pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt,
......
......@@ -20,7 +20,6 @@
use ast::{Ident, Mrk, Name, SyntaxContext};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};
......@@ -105,16 +104,8 @@ pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext
/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
local_data_key!(sctable_key: Rc<SCTable>)
match sctable_key.get() {
Some(ts) => op(&**ts),
None => {
let ts = Rc::new(new_sctable_internal());
sctable_key.replace(Some(ts.clone()));
op(&*ts)
}
}
thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal())
SCTABLE_KEY.with(|slot| op(slot))
}
// Make a fresh syntax context table with EmptyCtxt in slot zero
......@@ -165,16 +156,11 @@ pub fn resolve(id: Ident) -> Name {
// okay, I admit, putting this in TLS is not so nice:
// fetch the SCTable from TLS, create one if it doesn't yet exist.
fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
match resolve_table_key.get() {
Some(ts) => op(&mut *ts.borrow_mut()),
None => {
let ts = Rc::new(RefCell::new(HashMap::new()));
resolve_table_key.replace(Some(ts.clone()));
op(&mut *ts.borrow_mut())
}
}
thread_local!(static RESOLVE_TABLE_KEY: RefCell<ResolveTable> = {
RefCell::new(HashMap::new())
})
RESOLVE_TABLE_KEY.with(|slot| op(&mut *slot.borrow_mut()))
}
/// Resolve a syntax object to a name, per MTWT.
......
......@@ -560,15 +560,10 @@ pub mod keywords {
// fresh one.
// FIXME(eddyb) #8726 This should probably use a task-local reference.
pub fn get_ident_interner() -> Rc<IdentInterner> {
local_data_key!(key: Rc<::parse::token::IdentInterner>)
match key.get() {
Some(interner) => interner.clone(),
None => {
let interner = Rc::new(mk_fresh_ident_interner());
key.replace(Some(interner.clone()));
interner
}
}
thread_local!(static KEY: Rc<::parse::token::IdentInterner> = {
Rc::new(mk_fresh_ident_interner())
})
KEY.with(|k| k.clone())
}
/// Represents a string stored in the task-local interner. Because the
......
......@@ -15,6 +15,7 @@
extern crate rustc;
use std::any::Any;
use std::cell::RefCell;
use rustc::plugin::Registry;
struct Foo {
......@@ -27,7 +28,7 @@ fn drop(&mut self) {}
#[plugin_registrar]
pub fn registrar(_: &mut Registry) {
local_data_key!(foo: Box<Any+Send>);
foo.replace(Some(box Foo { foo: 10 } as Box<Any+Send>));
thread_local!(static FOO: RefCell<Option<Box<Any+Send>>> = RefCell::new(None));
FOO.with(|s| *s.borrow_mut() = Some(box Foo { foo: 10 } as Box<Any+Send>));
}
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Testing that we can't store a reference in task-local storage
local_data_key!(key: Box<&int>)
//~^ ERROR missing lifetime specifier
fn main() {}
......@@ -11,10 +11,10 @@
// check that the local data keys are private by default.
mod bar {
local_data_key!(baz: f64)
thread_local!(static baz: f64 = 0.0)
}
fn main() {
bar::baz.replace(Some(-10.0));
bar::baz.with(|_| ());
//~^ ERROR static `baz` is private
}
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
local_data_key!(foo: int)
mod bar {
local_data_key!(pub baz: f64)
}
pub fn main() {
assert!(foo.get().is_none());
assert!(bar::baz.get().is_none());
foo.replace(Some(3));
bar::baz.replace(Some(-10.0));
assert_eq!(*foo.get().unwrap(), 3);
assert_eq!(*bar::baz.get().unwrap(), -10.0);
}
......@@ -17,8 +17,6 @@
use rustrt::unwind::try;
local_data_key!(foo: int)
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
if argc > 1 {
......@@ -30,8 +28,6 @@ fn start(argc: int, argv: *const *const u8) -> int {
4 => assert!(try(|| panic!()).is_err()),
5 => assert!(try(|| spawn(proc() {})).is_err()),
6 => assert!(Command::new("test").spawn().is_err()),
7 => assert!(foo.get().is_none()),
8 => assert!(try(|| { foo.replace(Some(3)); }).is_err()),
_ => panic!()
}
}
......@@ -57,10 +53,6 @@ fn main() {
pass(Command::new(me).arg(x).output().unwrap());
let x: &[u8] = &[6u8];
pass(Command::new(me).arg(x).output().unwrap());
let x: &[u8] = &[7u8];
pass(Command::new(me).arg(x).output().unwrap());
let x: &[u8] = &[8u8];
pass(Command::new(me).arg(x).output().unwrap());
}
fn pass(output: ProcessOutput) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册