]> git.lizzy.rs Git - rust.git/commitdiff
std: Add a new top-level thread_local module
authorAlex Crichton <alex@alexcrichton.com>
Fri, 14 Nov 2014 22:20:57 +0000 (14:20 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 24 Nov 2014 07:37:16 +0000 (23:37 -0800)
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 thread local 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]

38 files changed:
src/liblog/lib.rs
src/librustc/util/common.rs
src/librustc_trans/trans/base.rs
src/librustdoc/html/format.rs
src/librustdoc/html/markdown.rs
src/librustdoc/html/render.rs
src/librustdoc/lib.rs
src/librustdoc/markdown.rs
src/librustdoc/passes.rs
src/librustdoc/stability_summary.rs
src/librustrt/lib.rs
src/librustrt/local_data.rs [deleted file]
src/librustrt/task.rs
src/libstd/collections/hash/map.rs
src/libstd/failure.rs
src/libstd/io/stdio.rs
src/libstd/lib.rs
src/libstd/macros.rs
src/libstd/rand/mod.rs
src/libstd/sync/future.rs
src/libstd/sys/common/mod.rs
src/libstd/sys/common/thread_local.rs [new file with mode: 0644]
src/libstd/sys/unix/mod.rs
src/libstd/sys/unix/thread_local.rs [new file with mode: 0644]
src/libstd/sys/windows/mod.rs
src/libstd/sys/windows/thread_local.rs [new file with mode: 0644]
src/libstd/thread_local/mod.rs [new file with mode: 0644]
src/libstd/thread_local/scoped.rs [new file with mode: 0644]
src/libsyntax/attr.rs
src/libsyntax/diagnostics/plugin.rs
src/libsyntax/ext/mtwt.rs
src/libsyntax/parse/token.rs
src/test/auxiliary/plugin_crate_outlive_expansion_phase.rs
src/test/compile-fail/core-tls-store-pointer.rs [deleted file]
src/test/compile-fail/macro-local-data-key-priv.rs
src/test/run-pass/macro-local-data-key.rs [deleted file]
src/test/run-pass/panic-during-tld-destroy.rs [deleted file]
src/test/run-pass/running-with-no-runtime.rs

index fd2d97d4deb5c1d8e6186e2681fb2be6d62f60c3..dab033e09729e41d317d95836dac31178069f987 100644 (file)
 
 extern crate regex;
 
-use regex::Regex;
+use std::cell::RefCell;
 use std::fmt;
 use std::io::LineBufferedWriter;
 use std::io;
 use std::slice;
 use std::sync::{Once, ONCE_INIT};
 
+use regex::Regex;
+
 use directive::LOG_LEVEL_NAMES;
 
 pub mod macros;
 /// 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
index e2fa02584f4fd70be3c9db8a37837be299b13ee6..7973004d5151997163e4b710b9abc4e57bac2633 100644 (file)
@@ -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};
 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
 }
index 85085f46731e4069677e6b25ea131a3916881a42..bdf2eca21d6eca514103f0880b5bdacfbe5edb11 100644 (file)
 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: () }
 }
 
index a7f33151547564a342927aa4edcdabe386af0208..2b521d1da06b3f17c1723beed56fd2f7a93e30d2 100644 (file)
@@ -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));
index 07b58e1b66c2aa22275a3606116921327c14f43e..11dc8f4f6603701d664d9fd0629f514f83a385b1 100644 (file)
@@ -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> {
index 9e3c336a7a06cc3f5c4619fb22c3efaf0bbf4f64..466af36898e0a1bd292e50fec1a345eead728b3b 100644 (file)
 //! 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())
+}
index 8770e473dea69a2317cd53f86fb284edea506158..204f866e82720ea24eb19d0226dd4b9802e20922 100644 (file)
 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,
index 7ee58d99c27efd087cb357972e80f7f142717e10..881c7a91d81d033b2a144fadab2c5799862ac790 100644 (file)
@@ -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());
 
index 7c4d28d5adb6bab566f09fb78dd203b72dddd754..f55d447f56927b870bad51a6e434e37843487d35 100644 (file)
@@ -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
index 7ec59f0139d9823eb7e22c52ad1541819cff0f0d..42f4c2a0ca63a19dacd2c03c3eac921e893a31c6 100644 (file)
@@ -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());
index 65e6bdb70f818babdb017c2dde3130fe238217d2..3d172d623501cfe99b2898da2a529b2e241a9688 100644 (file)
@@ -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;
diff --git a/src/librustrt/local_data.rs b/src/librustrt/local_data.rs
deleted file mode 100644 (file)
index ca0f694..0000000
+++ /dev/null
@@ -1,696 +0,0 @@
-// Copyright 2012-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.
-
-/*!
-
-Task local data management
-
-Allows storing arbitrary types inside task-local-data (TLD), to be accessed
-anywhere within a task, keyed by a global pointer parameterized over the type of
-the TLD slot. Useful for dynamic variables, singletons, and interfacing with
-foreign code with bad callback interfaces.
-
-To declare a new key for storing local data of a particular type, use the
-`local_data_key!` macro. This macro will expand to a `static` item appropriately
-named and annotated. This name is then passed to the functions in this module to
-modify/read the slot specified by the key.
-
-```rust
-local_data_key!(key_int: int)
-local_data_key!(key_vector: Vec<int>)
-
-key_int.replace(Some(3));
-assert_eq!(*key_int.get().unwrap(), 3);
-
-key_vector.replace(Some(vec![4]));
-assert_eq!(*key_vector.get().unwrap(), vec![4]);
-```
-
-*/
-
-// Casting 'Arcane Sight' reveals an overwhelming aura of Transmutation
-// magic.
-
-pub use self::KeyValue::*;
-
-use core::prelude::*;
-
-use alloc::heap;
-use collections::TreeMap;
-use core::cmp;
-use core::kinds::marker;
-use core::mem;
-use core::ptr;
-use core::fmt;
-use core::cell::UnsafeCell;
-
-use local::Local;
-use task::{Task, LocalStorage};
-
-/**
- * Indexes a task-local data slot. This pointer is used for comparison to
- * differentiate keys from one another. The actual type `T` is not used anywhere
- * as a member of this type, except that it is parameterized with it to define
- * the type of each key's value.
- *
- * The value of each Key is of the singleton enum KeyValue. These also have the
- * same name as `Key` and their purpose is to take up space in the programs data
- * sections to ensure that each value of the `Key` type points to a unique
- * location.
- */
-pub type Key<T> = &'static KeyValue<T>;
-
-#[allow(missing_docs)]
-pub enum KeyValue<T> { KeyValueKey }
-
-// The task-local-map stores all TLD information for the currently running
-// task. It is stored as an owned pointer into the runtime, and it's only
-// allocated when TLD is used for the first time.
-//
-// TLD values are boxed up, with a loan count stored in the box. The box is
-// necessary given how TLD maps are constructed, but theoretically in the
-// future this could be rewritten to statically construct TLD offsets at
-// compile-time to get O(1) lookup. At that time, the box can be removed.
-//
-// A very common usage pattern for TLD is to use replace(None) to extract a
-// value from TLD, work with it, and then store it (or a derived/new value)
-// back with replace(v). We take special care to reuse the allocation in this
-// case for performance reasons.
-//
-// However, that does mean that if a value is replaced with None, the
-// allocation will stay alive and the entry will stay in the TLD map until the
-// task deallocates. This makes the assumption that every key inserted into a
-// given task's TLD is going to be present for a majority of the rest of the
-// task's lifetime, but that's a fairly safe assumption, and there's very
-// little downside as long as it holds true for most keys.
-//
-// The Map type must be public in order to allow rustrt to see it.
-//
-// We'd like to use HashMap here, but it uses TLD in its construction (it uses
-// the task-local rng). We could try to provide our own source of randomness,
-// except it also lives in libstd (which is a client of us) so we can't even
-// reference it. Instead, use TreeMap, which provides reasonable performance.
-#[doc(hidden)]
-pub type Map = TreeMap<uint, TLDValue>;
-#[unsafe_no_drop_flag]
-struct TLDValue {
-    // box_ptr is a pointer to TLDValueBox<T>. It can never be null.
-    box_ptr: *mut (),
-    // drop_fn is the function that knows how to drop the box_ptr.
-    drop_fn: unsafe fn(p: *mut ())
-}
-
-struct TLDValueBox<T> {
-    // value is only initialized when refcount >= 1.
-    value: T,
-    // refcount of 0 means uninitialized value, 1 means initialized, 2+ means
-    // borrowed.
-    // NB: we use UnsafeCell instead of Cell because Ref should be allowed to
-    // be Sync. The only mutation occurs when a Ref is created or destroyed,
-    // so there's no issue with &Ref being thread-safe.
-    refcount: UnsafeCell<uint>
-}
-
-// Gets the map from the runtime. Lazily initialises if not done so already.
-unsafe fn get_local_map<'a>() -> Option<&'a mut Map> {
-    if !Local::exists(None::<Task>) { return None }
-
-    let task: *mut Task = Local::unsafe_borrow();
-    match &mut (*task).storage {
-        // If the at_exit function is already set, then we just need to take
-        // a loan out on the TLD map stored inside
-        &LocalStorage(Some(ref mut map_ptr)) => {
-            return Some(map_ptr);
-        }
-        // If this is the first time we've accessed TLD, perform similar
-        // actions to the oldsched way of doing things.
-        &LocalStorage(ref mut slot) => {
-            *slot = Some(TreeMap::new());
-            match *slot {
-                Some(ref mut map_ptr) => { return Some(map_ptr) }
-                None => panic!("unreachable code"),
-            }
-        }
-    }
-}
-
-/// A RAII immutable reference to a task-local value.
-///
-/// The task-local data can be accessed through this value, and when this
-/// structure is dropped it will return the borrow on the data.
-pub struct Ref<T:'static> {
-    // FIXME #12808: strange names to try to avoid interfering with
-    // field accesses of the contained type via Deref
-    _inner: &'static TLDValueBox<T>,
-    _marker: marker::NoSend
-}
-
-fn key_to_key_value<T: 'static>(key: Key<T>) -> uint {
-    key as *const _ as uint
-}
-
-impl<T: 'static> KeyValue<T> {
-    /// Replaces a value in task local data.
-    ///
-    /// If this key is already present in TLD, then the previous value is
-    /// replaced with the provided data, and then returned.
-    ///
-    /// # Panics
-    ///
-    /// This function will panic if the key is present in TLD and currently on
-    /// loan with the `get` method.
-    ///
-    /// It will also panic if there is no local task (because the current thread
-    /// is not owned by the runtime).
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// local_data_key!(foo: int)
-    ///
-    /// assert_eq!(foo.replace(Some(10)), None);
-    /// assert_eq!(foo.replace(Some(4)), Some(10));
-    /// assert_eq!(foo.replace(None), Some(4));
-    /// ```
-    pub fn replace(&'static self, data: Option<T>) -> Option<T> {
-        let map = match unsafe { get_local_map() } {
-            Some(map) => map,
-            None => panic!("must have a local task to insert into TLD"),
-        };
-        let keyval = key_to_key_value(self);
-
-        // The following match takes a mutable borrow on the map. In order to insert
-        // our data if the key isn't present, we need to let the match end first.
-        let data = match (map.get_mut(&keyval), data) {
-            (None, Some(data)) => {
-                // The key doesn't exist and we need to insert it. To make borrowck
-                // happy, return it up a scope and insert it there.
-                data
-            }
-            (None, None) => {
-                // The key doesn't exist and we're trying to replace it with nothing.
-                // Do nothing.
-                return None
-            }
-            (Some(slot), data) => {
-                // We have a slot with a box.
-                let value_box = slot.box_ptr as *mut TLDValueBox<T>;
-                let refcount = unsafe { *(*value_box).refcount.get() };
-                return match (refcount, data) {
-                    (0, None) => {
-                        // The current value is uninitialized and we have no new value.
-                        // Do nothing.
-                        None
-                    }
-                    (0, Some(new_value)) => {
-                        // The current value is uninitialized and we're storing a new value.
-                        unsafe {
-                            ptr::write(&mut (*value_box).value, new_value);
-                            *(*value_box).refcount.get() = 1;
-                            None
-                        }
-                    }
-                    (1, None) => {
-                        // We have an initialized value and we're removing it.
-                        unsafe {
-                            let ret = ptr::read(&(*value_box).value);
-                            *(*value_box).refcount.get() = 0;
-                            Some(ret)
-                        }
-                    }
-                    (1, Some(new_value)) => {
-                        // We have an initialized value and we're replacing it.
-                        let value_ref = unsafe { &mut (*value_box).value };
-                        let ret = mem::replace(value_ref, new_value);
-                        // Refcount is already 1, leave it as that.
-                        Some(ret)
-                    }
-                    _ => {
-                        // Refcount is 2+, which means we have a live borrow.
-                        panic!("TLD value cannot be replaced because it is already borrowed");
-                    }
-                }
-            }
-        };
-        // If we've reached this point, we need to insert into the map.
-        map.insert(keyval, TLDValue::new(data));
-        None
-    }
-
-    /// Borrows a value from TLD.
-    ///
-    /// If `None` is returned, then this key is not present in TLD. If `Some`
-    /// is returned, then the returned data is a smart pointer representing a
-    /// new loan on this TLD key. While on loan, this key cannot be altered via
-    /// the `replace` method.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// local_data_key!(key: int)
-    ///
-    /// assert!(key.get().is_none());
-    ///
-    /// key.replace(Some(3));
-    /// assert_eq!(*key.get().unwrap(), 3);
-    /// ```
-    pub fn get(&'static self) -> Option<Ref<T>> {
-        let map = match unsafe { get_local_map() } {
-            Some(map) => map,
-            None => return None,
-        };
-        let keyval = key_to_key_value(self);
-
-        match map.get(&keyval) {
-            Some(slot) => {
-                let value_box = slot.box_ptr as *mut TLDValueBox<T>;
-                if unsafe { *(*value_box).refcount.get() } >= 1 {
-                    unsafe {
-                        *(*value_box).refcount.get() += 1;
-                        Some(Ref {
-                            _inner: &*value_box,
-                            _marker: marker::NoSend
-                        })
-                    }
-                } else {
-                    None
-                }
-            }
-            None => None
-        }
-    }
-
-    // it's not clear if this is the right design for a public API, or if
-    // there's even a need for this as a public API, but our benchmarks need
-    // this to ensure consistent behavior on each run.
-    #[cfg(test)]
-    fn clear(&'static self) {
-        let map = match unsafe { get_local_map() } {
-            Some(map) => map,
-            None => return
-        };
-        let keyval = key_to_key_value(self);
-        self.replace(None); // ensure we have no outstanding borrows
-        map.remove(&keyval);
-    }
-}
-
-impl<T: 'static> Deref<T> for Ref<T> {
-    #[inline(always)]
-    fn deref<'a>(&'a self) -> &'a T {
-        &self._inner.value
-    }
-}
-
-impl<T: 'static + fmt::Show> fmt::Show for Ref<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        (**self).fmt(f)
-    }
-}
-
-impl<T: cmp::PartialEq + 'static> cmp::PartialEq for Ref<T> {
-    fn eq(&self, other: &Ref<T>) -> bool {
-        (**self).eq(&**other)
-    }
-    fn ne(&self, other: &Ref<T>) -> bool {
-        (**self).ne(&**other)
-    }
-}
-
-impl<T: cmp::Eq + 'static> cmp::Eq for Ref<T> {}
-
-impl<T: cmp::PartialOrd + 'static> cmp::PartialOrd for Ref<T> {
-    fn partial_cmp(&self, other: &Ref<T>) -> Option<cmp::Ordering> {
-        (**self).partial_cmp(&**other)
-    }
-    fn lt(&self, other: &Ref<T>) -> bool { (**self).lt(&**other) }
-    fn le(&self, other: &Ref<T>) -> bool { (**self).le(&**other) }
-    fn gt(&self, other: &Ref<T>) -> bool { (**self).gt(&**other) }
-    fn ge(&self, other: &Ref<T>) -> bool { (**self).ge(&**other) }
-}
-
-impl<T: cmp::Ord + 'static> cmp::Ord for Ref<T> {
-    fn cmp(&self, other: &Ref<T>) -> cmp::Ordering {
-        (**self).cmp(&**other)
-    }
-}
-
-#[unsafe_destructor]
-impl<T: 'static> Drop for Ref<T> {
-    fn drop(&mut self) {
-        unsafe {
-            *self._inner.refcount.get() -= 1;
-        }
-    }
-}
-
-impl TLDValue {
-    fn new<T>(value: T) -> TLDValue {
-        let box_ptr = unsafe {
-            let allocation = heap::allocate(mem::size_of::<TLDValueBox<T>>(),
-                                            mem::min_align_of::<TLDValueBox<T>>());
-            if allocation.is_null() { ::alloc::oom() }
-            let value_box = allocation as *mut TLDValueBox<T>;
-            ptr::write(value_box, TLDValueBox {
-                value: value,
-                refcount: UnsafeCell::new(1)
-            });
-            value_box as *mut ()
-        };
-        // Destruction of TLDValue needs to know how to properly deallocate the TLDValueBox,
-        // so we need our own custom destructor function.
-        unsafe fn d<T>(p: *mut ()) {
-            let value_box = p as *mut TLDValueBox<T>;
-            debug_assert!(*(*value_box).refcount.get() < 2, "TLDValue destructed while borrowed");
-            // use a RAII type here to ensure we always deallocate even if we panic while
-            // running the destructor for the value.
-            struct Guard<T> {
-                p: *mut TLDValueBox<T>
-            }
-            #[unsafe_destructor]
-            impl<T> Drop for Guard<T> {
-                fn drop(&mut self) {
-                    let size = mem::size_of::<TLDValueBox<T>>();
-                    let align = mem::align_of::<TLDValueBox<T>>();
-                    unsafe { heap::deallocate(self.p as *mut u8, size, align); }
-                }
-            }
-            let _guard = Guard::<T> { p: value_box };
-            if *(*value_box).refcount.get() != 0 {
-                // the contained value is valid; drop it
-                ptr::read(&(*value_box).value);
-            }
-            // the box will be deallocated by the guard
-        }
-        TLDValue {
-            box_ptr: box_ptr,
-            drop_fn: d::<T>
-        }
-    }
-}
-
-
-impl Drop for TLDValue {
-    fn drop(&mut self) {
-        // box_ptr should always be non-null. Check it anyway just to be thorough
-        if !self.box_ptr.is_null() {
-            unsafe { (self.drop_fn)(self.box_ptr) }
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    extern crate test;
-
-    use std::prelude::*;
-    use super::*;
-    use std::task;
-
-    #[test]
-    fn test_tls_multitask() {
-        static MY_KEY: Key<String> = &KeyValueKey;
-        MY_KEY.replace(Some("parent data".to_string()));
-        task::spawn(proc() {
-            // TLD shouldn't carry over.
-            assert!(MY_KEY.get().is_none());
-            MY_KEY.replace(Some("child data".to_string()));
-            assert!(MY_KEY.get().as_ref().unwrap().as_slice() == "child data");
-            // should be cleaned up for us
-        });
-
-        // Must work multiple times
-        assert!(MY_KEY.get().unwrap().as_slice() == "parent data");
-        assert!(MY_KEY.get().unwrap().as_slice() == "parent data");
-        assert!(MY_KEY.get().unwrap().as_slice() == "parent data");
-    }
-
-    #[test]
-    fn test_tls_overwrite() {
-        static MY_KEY: Key<String> = &KeyValueKey;
-        MY_KEY.replace(Some("first data".to_string()));
-        MY_KEY.replace(Some("next data".to_string())); // Shouldn't leak.
-        assert!(MY_KEY.get().unwrap().as_slice() == "next data");
-    }
-
-    #[test]
-    fn test_tls_pop() {
-        static MY_KEY: Key<String> = &KeyValueKey;
-        MY_KEY.replace(Some("weasel".to_string()));
-        assert!(MY_KEY.replace(None).unwrap() == "weasel".to_string());
-        // Pop must remove the data from the map.
-        assert!(MY_KEY.replace(None).is_none());
-    }
-
-    #[test]
-    fn test_tls_crust_automorestack_memorial_bug() {
-        // This might result in a stack-canary clobber if the runtime fails to
-        // set sp_limit to 0 when calling the cleanup extern - it might
-        // automatically jump over to the rust stack, which causes next_c_sp
-        // to get recorded as something within a rust stack segment. Then a
-        // subsequent upcall (esp. for logging, think vsnprintf) would run on
-        // a stack smaller than 1 MB.
-        static MY_KEY: Key<String> = &KeyValueKey;
-        task::spawn(proc() {
-            MY_KEY.replace(Some("hax".to_string()));
-        });
-    }
-
-    #[test]
-    fn test_tls_multiple_types() {
-        static STR_KEY: Key<String> = &KeyValueKey;
-        static BOX_KEY: Key<Box<int>> = &KeyValueKey;
-        static INT_KEY: Key<int> = &KeyValueKey;
-        task::spawn(proc() {
-            STR_KEY.replace(Some("string data".to_string()));
-            BOX_KEY.replace(Some(box 0));
-            INT_KEY.replace(Some(42));
-        });
-    }
-
-    #[test]
-    fn test_tls_overwrite_multiple_types() {
-        static STR_KEY: Key<String> = &KeyValueKey;
-        static BOX_KEY: Key<Box<int>> = &KeyValueKey;
-        static INT_KEY: Key<int> = &KeyValueKey;
-        task::spawn(proc() {
-            STR_KEY.replace(Some("string data".to_string()));
-            STR_KEY.replace(Some("string data 2".to_string()));
-            BOX_KEY.replace(Some(box 0));
-            BOX_KEY.replace(Some(box 1));
-            INT_KEY.replace(Some(42));
-            // This could cause a segfault if overwriting-destruction is done
-            // with the crazy polymorphic transmute rather than the provided
-            // finaliser.
-            INT_KEY.replace(Some(31337));
-        });
-    }
-
-    #[test]
-    #[should_fail]
-    fn test_tls_cleanup_on_panic() {
-        static STR_KEY: Key<String> = &KeyValueKey;
-        static BOX_KEY: Key<Box<int>> = &KeyValueKey;
-        static INT_KEY: Key<int> = &KeyValueKey;
-        STR_KEY.replace(Some("parent data".to_string()));
-        BOX_KEY.replace(Some(box 0));
-        task::spawn(proc() {
-            STR_KEY.replace(Some("string data".to_string()));
-            BOX_KEY.replace(Some(box 2));
-            INT_KEY.replace(Some(42));
-            panic!();
-        });
-        // Not quite nondeterministic.
-        INT_KEY.replace(Some(31337));
-        panic!();
-    }
-
-    #[test]
-    fn test_cleanup_drops_values() {
-        let (tx, rx) = channel::<()>();
-        struct Dropper {
-            tx: Sender<()>
-        };
-        impl Drop for Dropper {
-            fn drop(&mut self) {
-                self.tx.send(());
-            }
-        }
-        static KEY: Key<Dropper> = &KeyValueKey;
-        let _ = task::try(proc() {
-            KEY.replace(Some(Dropper{ tx: tx }));
-        });
-        // At this point the task has been cleaned up and the TLD dropped.
-        // If the channel doesn't have a value now, then the Sender was leaked.
-        assert_eq!(rx.try_recv(), Ok(()));
-    }
-
-    #[test]
-    fn test_static_pointer() {
-        static KEY: Key<&'static int> = &KeyValueKey;
-        static VALUE: int = 0;
-        KEY.replace(Some(&VALUE));
-    }
-
-    #[test]
-    fn test_owned() {
-        static KEY: Key<Box<int>> = &KeyValueKey;
-        KEY.replace(Some(box 1));
-
-        {
-            let k1 = KEY.get().unwrap();
-            let k2 = KEY.get().unwrap();
-            let k3 = KEY.get().unwrap();
-            assert_eq!(**k1, 1);
-            assert_eq!(**k2, 1);
-            assert_eq!(**k3, 1);
-        }
-        KEY.replace(Some(box 2));
-        assert_eq!(**KEY.get().unwrap(), 2);
-    }
-
-    #[test]
-    fn test_same_key_type() {
-        static KEY1: Key<int> = &KeyValueKey;
-        static KEY2: Key<int> = &KeyValueKey;
-        static KEY3: Key<int> = &KeyValueKey;
-        static KEY4: Key<int> = &KeyValueKey;
-        static KEY5: Key<int> = &KeyValueKey;
-        KEY1.replace(Some(1));
-        KEY2.replace(Some(2));
-        KEY3.replace(Some(3));
-        KEY4.replace(Some(4));
-        KEY5.replace(Some(5));
-
-        assert_eq!(*KEY1.get().unwrap(), 1);
-        assert_eq!(*KEY2.get().unwrap(), 2);
-        assert_eq!(*KEY3.get().unwrap(), 3);
-        assert_eq!(*KEY4.get().unwrap(), 4);
-        assert_eq!(*KEY5.get().unwrap(), 5);
-    }
-
-    #[test]
-    #[should_fail]
-    fn test_nested_get_set1() {
-        static KEY: Key<int> = &KeyValueKey;
-        assert_eq!(KEY.replace(Some(4)), None);
-
-        let _k = KEY.get();
-        KEY.replace(Some(4));
-    }
-
-    // ClearKey is a RAII class that ensures the keys are cleared from the map.
-    // This is so repeated runs of a benchmark don't bloat the map with extra
-    // keys and distort the measurements.
-    // It's not used on the tests because the tests run in separate tasks.
-    struct ClearKey<T>(Key<T>);
-    #[unsafe_destructor]
-    impl<T: 'static> Drop for ClearKey<T> {
-        fn drop(&mut self) {
-            let ClearKey(ref key) = *self;
-            key.clear();
-        }
-    }
-
-    #[bench]
-    fn bench_replace_none(b: &mut test::Bencher) {
-        static KEY: Key<uint> = &KeyValueKey;
-        let _clear = ClearKey(KEY);
-        KEY.replace(None);
-        b.iter(|| {
-            KEY.replace(None)
-        });
-    }
-
-    #[bench]
-    fn bench_replace_some(b: &mut test::Bencher) {
-        static KEY: Key<uint> = &KeyValueKey;
-        let _clear = ClearKey(KEY);
-        KEY.replace(Some(1u));
-        b.iter(|| {
-            KEY.replace(Some(2))
-        });
-    }
-
-    #[bench]
-    fn bench_replace_none_some(b: &mut test::Bencher) {
-        static KEY: Key<uint> = &KeyValueKey;
-        let _clear = ClearKey(KEY);
-        KEY.replace(Some(0u));
-        b.iter(|| {
-            let old = KEY.replace(None).unwrap();
-            let new = old + 1;
-            KEY.replace(Some(new))
-        });
-    }
-
-    #[bench]
-    fn bench_100_keys_replace_last(b: &mut test::Bencher) {
-        static KEYS: [KeyValue<uint>, ..100] = [KeyValueKey, ..100];
-        let _clear = KEYS.iter().map(ClearKey).collect::<Vec<ClearKey<uint>>>();
-        for (i, key) in KEYS.iter().enumerate() {
-            key.replace(Some(i));
-        }
-        b.iter(|| {
-            let key: Key<uint> = &KEYS[99];
-            key.replace(Some(42))
-        });
-    }
-
-    #[bench]
-    fn bench_1000_keys_replace_last(b: &mut test::Bencher) {
-        static KEYS: [KeyValue<uint>, ..1000] = [KeyValueKey, ..1000];
-        let _clear = KEYS.iter().map(ClearKey).collect::<Vec<ClearKey<uint>>>();
-        for (i, key) in KEYS.iter().enumerate() {
-            key.replace(Some(i));
-        }
-        b.iter(|| {
-            let key: Key<uint> = &KEYS[999];
-            key.replace(Some(42))
-        });
-        for key in KEYS.iter() { key.clear(); }
-    }
-
-    #[bench]
-    fn bench_get(b: &mut test::Bencher) {
-        static KEY: Key<uint> = &KeyValueKey;
-        let _clear = ClearKey(KEY);
-        KEY.replace(Some(42));
-        b.iter(|| {
-            KEY.get()
-        });
-    }
-
-    #[bench]
-    fn bench_100_keys_get_last(b: &mut test::Bencher) {
-        static KEYS: [KeyValue<uint>, ..100] = [KeyValueKey, ..100];
-        let _clear = KEYS.iter().map(ClearKey).collect::<Vec<ClearKey<uint>>>();
-        for (i, key) in KEYS.iter().enumerate() {
-            key.replace(Some(i));
-        }
-        b.iter(|| {
-            let key: Key<uint> = &KEYS[99];
-            key.get()
-        });
-    }
-
-    #[bench]
-    fn bench_1000_keys_get_last(b: &mut test::Bencher) {
-        static KEYS: [KeyValue<uint>, ..1000] = [KeyValueKey, ..1000];
-        let _clear = KEYS.iter().map(ClearKey).collect::<Vec<ClearKey<uint>>>();
-        for (i, key) in KEYS.iter().enumerate() {
-            key.replace(Some(i));
-        }
-        b.iter(|| {
-            let key: Key<uint> = &KEYS[999];
-            key.get()
-        });
-    }
-}
index 64c402bfbbc3985c8293205f169629c3b9a755c7..63fa3938fc828c549b1e1ad4774b5861123d20a7 100644 (file)
@@ -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()());
index 69375e8d4f84e131d5044185422b130c96589472..662ae913764f4d6ec482d8a12fc2436708947d3f 100644 (file)
@@ -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]
index c23e043c174091876a60189b0c7f44a229148407..32a8be2290297cbde781bd333194eaf587e40d49 100644 (file)
 
 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 '{}', {}:{}",
index 7374668a69d834c1cbfa961e1a20ab2ace2c990e..d450e9f1dce3eebc17ac01c42804464f61d87191 100644 (file)
 
 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;
index b35c49efdd80cfee79aea45e806dd39ed5b7390c..77da727e94d319829ab174c1f4681c2ab5edccaf 100644 (file)
 pub use core_collections::vec;
 
 pub use rustrt::c_str;
-pub use rustrt::local_data;
 
 pub use unicode::char;
 
 #[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;
 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![]
+
 }
index 18c109d92a0116c72fc7d716dc22b5f3d4a50262..c3260225d0a3f15ff2a59b7a5e897daba3b9c7f0 100644 (file)
@@ -304,28 +304,6 @@ macro_rules! println(
     ($($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`.
index 08eb7350bcf385c6618947cc26e57eb41b5c0ba7..f9f9147b1071b757710520cc0802e40c41bf5782 100644 (file)
 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 {
index e37d1f83877967767e80eabd5d4cb9e8de2593be..3e1ba8cebf814261e94d373cc6c1d844b3487bfb 100644 (file)
@@ -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());
-    }
 }
index cacb128faa560438f5447a50112be3d3a96f0dea..77edd7c5c4eb6db8ca3abb88cfc25ecfe021fa12 100644 (file)
@@ -21,6 +21,7 @@
 
 pub mod net;
 pub mod helper_thread;
+pub mod thread_local;
 
 // common error constructors
 
diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs
new file mode 100644 (file)
index 0000000..9ad38cb
--- /dev/null
@@ -0,0 +1,306 @@
+// 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);
+        }
+    }
+}
+
index 664a6a1e70c767b1b3b1f17d6a4b48782642f462..7a5317b578d9346244e038de35b43617545cc13f 100644 (file)
@@ -34,14 +34,15 @@ macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
 
 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;
diff --git a/src/libstd/sys/unix/thread_local.rs b/src/libstd/sys/unix/thread_local.rs
new file mode 100644 (file)
index 0000000..b300e93
--- /dev/null
@@ -0,0 +1,52 @@
+// 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::c_int;
+
+pub type Key = pthread_key_t;
+
+#[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;
+}
+
+#[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)
+}
+
+#[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;
+}
index 815ace21f879d931f80bbe8e2a5f2b915b7a86b2..a785ccfe80465ff8891518d3ccdf70098ef9ba3f 100644 (file)
@@ -35,14 +35,15 @@ macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
 
 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;
diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs
new file mode 100644 (file)
index 0000000..b841f6d
--- /dev/null
@@ -0,0 +1,238 @@
+// 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;
+            }
+        }
+    }
+}
diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs
new file mode 100644 (file)
index 0000000..e2e8461
--- /dev/null
@@ -0,0 +1,634 @@
+// 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.
+
+//! Thread local storage
+//!
+//! This module provides an implementation of thread local storage for Rust
+//! programs. Thread local storage is a method of storing data into a global
+//! variable which each thread in the program will have its own copy of.
+//! Threads do not share this data, so accesses do not need to be synchronized.
+//!
+//! At a high level, this module provides two variants of storage:
+//!
+//! * Owning thread local storage. This is a type of thread local key which
+//!   owns the value that it contains, and will destroy the value when the
+//!   thread exits. This variant is created with the `thread_local!` macro and
+//!   can contain any value which is `'static` (no borrowed pointers.
+//!
+//! * Scoped thread local storage. This type of key is used to store a reference
+//!   to a value into local storage temporarily for the scope of a function
+//!   call. There are no restrictions on what types of values can be placed
+//!   into this key.
+//!
+//! Both forms of thread local storage provide an accessor function, `with`,
+//! which will yield a shared reference to the value to the specified
+//! closure. Thread local keys only allow shared access to values as there is no
+//! way to guarantee uniqueness if a mutable borrow was allowed. Most values
+//! will want to make use of some form of **interior mutability** through the
+//! `Cell` or `RefCell` types.
+
+#![macro_escape]
+#![experimental]
+
+use prelude::*;
+
+use cell::UnsafeCell;
+
+// Sure wish we had macro hygiene, no?
+#[doc(hidden)] pub use self::imp::Key as KeyInner;
+#[doc(hidden)] pub use self::imp::destroy_value;
+#[doc(hidden)] pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER;
+#[doc(hidden)] pub use sys_common::thread_local::StaticKey as OsStaticKey;
+
+pub mod scoped;
+
+/// A thread local storage key which owns its contents.
+///
+/// This key uses the fastest possible implementation available to it for the
+/// target platform. It is instantiated with the `thread_local!` macro and the
+/// primary method is the `with` method.
+///
+/// The `with` method yields a reference to the contained value which cannot be
+/// sent across tasks or escape the given closure.
+///
+/// # Initialization and Destruction
+///
+/// Initialization is dynamically performed on the first call to `with()`
+/// within a thread, and values support destructors which will be run when a
+/// thread exits.
+///
+/// # Example
+///
+/// ```
+/// use std::cell::RefCell;
+///
+/// thread_local!(static FOO: RefCell<uint> = RefCell::new(1));
+///
+/// FOO.with(|f| {
+///     assert_eq!(*f.borrow(), 1);
+///     *f.borrow_mut() = 2;
+/// });
+///
+/// // each thread starts out with the initial value of 1
+/// spawn(proc() {
+///     FOO.with(|f| {
+///         assert_eq!(*f.borrow(), 1);
+///         *f.borrow_mut() = 3;
+///     });
+/// });
+///
+/// // we retain our original value of 2 despite the child thread
+/// FOO.with(|f| {
+///     assert_eq!(*f.borrow(), 2);
+/// });
+/// ```
+pub struct Key<T> {
+    // The key itself may be tagged with #[thread_local], and this `Key` is
+    // stored as a `static`, and it's not valid for a static to reference the
+    // address of another thread_local static. For this reason we kinda wonkily
+    // work around this by generating a shim function which will give us the
+    // address of the inner TLS key at runtime.
+    //
+    // This is trivially devirtualizable by LLVM because we never store anything
+    // to this field and rustc can declare the `static` as constant as well.
+    #[doc(hidden)]
+    pub inner: fn() -> &'static KeyInner<UnsafeCell<Option<T>>>,
+
+    // initialization routine to invoke to create a value
+    #[doc(hidden)]
+    pub init: fn() -> T,
+}
+
+/// Declare a new thread local storage key of type `std::thread_local::Key`.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! thread_local(
+    (static $name:ident: $t:ty = $init:expr) => (
+        static $name: ::std::thread_local::Key<$t> = {
+            use std::cell::UnsafeCell as __UnsafeCell;
+            use std::thread_local::KeyInner as __KeyInner;
+            use std::option::Option as __Option;
+            use std::option::None as __None;
+
+            __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = {
+                __UnsafeCell { value: __None }
+            })
+            fn __init() -> $t { $init }
+            fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> {
+                &__KEY
+            }
+            ::std::thread_local::Key { inner: __getit, init: __init }
+        };
+    );
+    (pub static $name:ident: $t:ty = $init:expr) => (
+        pub static $name: ::std::thread_local::Key<$t> = {
+            use std::cell::UnsafeCell as __UnsafeCell;
+            use std::thread_local::KeyInner as __KeyInner;
+            use std::option::Option as __Option;
+            use std::option::None as __None;
+
+            __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = {
+                __UnsafeCell { value: __None }
+            })
+            fn __init() -> $t { $init }
+            fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> {
+                &__KEY
+            }
+            ::std::thread_local::Key { inner: __getit, init: __init }
+        };
+    );
+)
+
+// Macro pain #4586:
+//
+// When cross compiling, rustc will load plugins and macros from the *host*
+// platform before search for macros from the target platform. This is primarily
+// done to detect, for example, plugins. Ideally the macro below would be
+// defined once per module below, but unfortunately this means we have the
+// following situation:
+//
+// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro
+//    will inject #[thread_local] statics.
+// 2. We then try to compile a program for arm-linux-androideabi
+// 3. The compiler has a host of linux and a target of android, so it loads
+//    macros from the *linux* libstd.
+// 4. The macro generates a #[thread_local] field, but the android libstd does
+//    not use #[thread_local]
+// 5. Compile error about structs with wrong fields.
+//
+// To get around this, we're forced to inject the #[cfg] logic into the macro
+// itself. Woohoo.
+
+#[macro_export]
+macro_rules! __thread_local_inner(
+    (static $name:ident: $t:ty = $init:expr) => (
+        #[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)]
+        static $name: ::std::thread_local::KeyInner<$t> =
+            __thread_local_inner!($init, $t);
+    );
+    (pub static $name:ident: $t:ty = $init:expr) => (
+        #[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)]
+        pub static $name: ::std::thread_local::KeyInner<$t> =
+            __thread_local_inner!($init, $t);
+    );
+    ($init:expr, $t:ty) => ({
+        #[cfg(any(target_os = "macos", target_os = "linux"))]
+        const INIT: ::std::thread_local::KeyInner<$t> = {
+            ::std::thread_local::KeyInner {
+                inner: ::std::cell::UnsafeCell { value: $init },
+                dtor_registered: ::std::cell::UnsafeCell { value: false },
+                dtor_running: ::std::cell::UnsafeCell { value: false },
+                marker: ::std::kinds::marker::NoCopy,
+            }
+        };
+
+        #[cfg(not(any(target_os = "macos", target_os = "linux")))]
+        const INIT: ::std::thread_local::KeyInner<$t> = {
+            unsafe extern fn __destroy(ptr: *mut u8) {
+                ::std::thread_local::destroy_value::<$t>(ptr);
+            }
+            ::std::thread_local::KeyInner {
+                inner: ::std::cell::UnsafeCell { value: $init },
+                os: ::std::thread_local::OsStaticKey {
+                    inner: ::std::thread_local::OS_INIT_INNER,
+                    dtor: ::std::option::Some(__destroy),
+                },
+            }
+        };
+
+        INIT
+    });
+)
+
+impl<T: 'static> Key<T> {
+    /// Acquire a reference to the value in this TLS key.
+    ///
+    /// This will lazily initialize the value if this thread has not referenced
+    /// this key yet.
+    ///
+    /// # Panics
+    ///
+    /// This function will `panic!()` if the key currently has its
+    /// destructor running, and it **may** panic if the destructor has
+    /// previously been run for this thread.
+    pub fn with<R>(&'static self, f: |&T| -> R) -> R {
+        let slot = (self.inner)();
+        unsafe {
+            let slot = slot.get().expect("cannot access a TLS value during or \
+                                          after it is destroyed");
+            if (*slot.get()).is_none() {
+                *slot.get() = Some((self.init)());
+            }
+            f((*slot.get()).as_ref().unwrap())
+        }
+    }
+
+    /// Test this TLS key to determine whether its value has been destroyed for
+    /// the current thread or not.
+    ///
+    /// This will not initialize the key if it is not already initialized.
+    pub fn destroyed(&'static self) -> bool {
+        unsafe { (self.inner)().get().is_none() }
+    }
+}
+
+#[cfg(any(target_os = "macos", target_os = "linux"))]
+mod imp {
+    use prelude::*;
+
+    use cell::UnsafeCell;
+    use intrinsics;
+    use kinds::marker;
+    use ptr;
+
+    #[doc(hidden)]
+    pub struct Key<T> {
+        // Place the inner bits in an `UnsafeCell` to currently get around the
+        // "only Sync statics" restriction. This allows any type to be placed in
+        // the cell.
+        //
+        // Note that all access requires `T: 'static` so it can't be a type with
+        // any borrowed pointers still.
+        pub inner: UnsafeCell<T>,
+
+        // Metadata to keep track of the state of the destructor. Remember that
+        // these variables are thread-local, not global.
+        pub dtor_registered: UnsafeCell<bool>, // should be Cell
+        pub dtor_running: UnsafeCell<bool>, // should be Cell
+
+        // These shouldn't be copied around.
+        pub marker: marker::NoCopy,
+    }
+
+    #[doc(hidden)]
+    impl<T> Key<T> {
+        pub unsafe fn get(&'static self) -> Option<&'static T> {
+            if intrinsics::needs_drop::<T>() && *self.dtor_running.get() {
+                return None
+            }
+            self.register_dtor();
+            Some(&*self.inner.get())
+        }
+
+        unsafe fn register_dtor(&self) {
+            if !intrinsics::needs_drop::<T>() || *self.dtor_registered.get() {
+                return
+            }
+
+            register_dtor(self as *const _ as *mut u8,
+                          destroy_value::<T>);
+            *self.dtor_registered.get() = true;
+        }
+    }
+
+    // Since what appears to be glibc 2.18 this symbol has been shipped which
+    // GCC and clang both use to invoke destructors in thread_local globals, so
+    // let's do the same!
+    //
+    // Note, however, that we run on lots older linuxes, as well as cross
+    // compiling from a newer linux to an older linux, so we also have a
+    // fallback implementation to use as well.
+    //
+    // Due to rust-lang/rust#18804, make sure this is not generic!
+    #[cfg(target_os = "linux")]
+    unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+        use mem;
+        use libc;
+        use sys_common::thread_local as os;
+
+        extern {
+            static __dso_handle: *mut u8;
+            #[linkage = "extern_weak"]
+            static __cxa_thread_atexit_impl: *const ();
+        }
+        if !__cxa_thread_atexit_impl.is_null() {
+            type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
+                                      arg: *mut u8,
+                                      dso_handle: *mut u8) -> libc::c_int;
+            mem::transmute::<*const (), F>(__cxa_thread_atexit_impl)
+            (dtor, t, __dso_handle);
+            return
+        }
+
+        // The fallback implementation uses a vanilla OS-based TLS key to track
+        // the list of destructors that need to be run for this thread. The key
+        // then has its own destructor which runs all the other destructors.
+        //
+        // The destructor for DTORS is a little special in that it has a `while`
+        // loop to continuously drain the list of registered destructors. It
+        // *should* be the case that this loop always terminates because we
+        // provide the guarantee that a TLS key cannot be set after it is
+        // flagged for destruction.
+        static DTORS: os::StaticKey = os::StaticKey {
+            inner: os::INIT_INNER,
+            dtor: Some(run_dtors),
+        };
+        type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
+        if DTORS.get().is_null() {
+            let v: Box<List> = box Vec::new();
+            DTORS.set(mem::transmute(v));
+        }
+        let list: &mut List = &mut *(DTORS.get() as *mut List);
+        list.push((t, dtor));
+
+        unsafe extern fn run_dtors(mut ptr: *mut u8) {
+            while !ptr.is_null() {
+                let list: Box<List> = mem::transmute(ptr);
+                for &(ptr, dtor) in list.iter() {
+                    dtor(ptr);
+                }
+                ptr = DTORS.get();
+                DTORS.set(0 as *mut _);
+            }
+        }
+    }
+
+    // OSX's analog of the above linux function is this _tlv_atexit function.
+    // The disassembly of thread_local globals in C++ (at least produced by
+    // clang) will have this show up in the output.
+    #[cfg(target_os = "macos")]
+    unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+        extern {
+            fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
+                           arg: *mut u8);
+        }
+        _tlv_atexit(dtor, t);
+    }
+
+    #[doc(hidden)]
+    pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
+        let ptr = ptr as *mut Key<T>;
+        // Right before we run the user destructor be sure to flag the
+        // destructor as running for this thread so calls to `get` will return
+        // `None`.
+        *(*ptr).dtor_running.get() = true;
+        ptr::read((*ptr).inner.get() as *const T);
+    }
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "linux")))]
+mod imp {
+    use prelude::*;
+
+    use cell::UnsafeCell;
+    use mem;
+    use sys_common::thread_local::StaticKey as OsStaticKey;
+
+    #[doc(hidden)]
+    pub struct Key<T> {
+        // Statically allocated initialization expression, using an `UnsafeCell`
+        // for the same reasons as above.
+        pub inner: UnsafeCell<T>,
+
+        // OS-TLS key that we'll use to key off.
+        pub os: OsStaticKey,
+    }
+
+    struct Value<T: 'static> {
+        key: &'static Key<T>,
+        value: T,
+    }
+
+    #[doc(hidden)]
+    impl<T> Key<T> {
+        pub unsafe fn get(&'static self) -> Option<&'static T> {
+            self.ptr().map(|p| &*p)
+        }
+
+        unsafe fn ptr(&'static self) -> Option<*mut T> {
+            let ptr = self.os.get() as *mut Value<T>;
+            if !ptr.is_null() {
+                if ptr as uint == 1 {
+                    return None
+                }
+                return Some(&mut (*ptr).value as *mut T);
+            }
+
+            // If the lookup returned null, we haven't initialized our own local
+            // copy, so do that now.
+            //
+            // Also note that this transmute_copy should be ok because the value
+            // `inner` is already validated to be a valid `static` value, so we
+            // should be able to freely copy the bits.
+            let ptr: Box<Value<T>> = box Value {
+                key: self,
+                value: mem::transmute_copy(&self.inner),
+            };
+            let ptr: *mut Value<T> = mem::transmute(ptr);
+            self.os.set(ptr as *mut u8);
+            Some(&mut (*ptr).value as *mut T)
+        }
+    }
+
+    #[doc(hidden)]
+    pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
+        // The OS TLS ensures that this key contains a NULL value when this
+        // destructor starts to run. We set it back to a sentinel value of 1 to
+        // ensure that any future calls to `get` for this thread will return
+        // `None`.
+        //
+        // Note that to prevent an infinite loop we reset it back to null right
+        // before we return from the destructor ourselves.
+        let ptr: Box<Value<T>> = mem::transmute(ptr);
+        let key = ptr.key;
+        key.os.set(1 as *mut u8);
+        drop(ptr);
+        key.os.set(0 as *mut u8);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::*;
+
+    use cell::UnsafeCell;
+    use rustrt::thread::Thread;
+
+    struct Foo(Sender<()>);
+
+    impl Drop for Foo {
+        fn drop(&mut self) {
+            let Foo(ref s) = *self;
+            s.send(());
+        }
+    }
+
+    #[test]
+    fn smoke_no_dtor() {
+        thread_local!(static FOO: UnsafeCell<int> = UnsafeCell { value: 1 })
+
+        FOO.with(|f| unsafe {
+            assert_eq!(*f.get(), 1);
+            *f.get() = 2;
+        });
+        let (tx, rx) = channel();
+        spawn(proc() {
+            FOO.with(|f| unsafe {
+                assert_eq!(*f.get(), 1);
+            });
+            tx.send(());
+        });
+        rx.recv();
+
+        FOO.with(|f| unsafe {
+            assert_eq!(*f.get(), 2);
+        });
+    }
+
+    #[test]
+    fn smoke_dtor() {
+        thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell {
+            value: None
+        })
+
+        let (tx, rx) = channel();
+        spawn(proc() unsafe {
+            let mut tx = Some(tx);
+            FOO.with(|f| {
+                *f.get() = Some(Foo(tx.take().unwrap()));
+            });
+        });
+        rx.recv();
+    }
+
+    #[test]
+    fn circular() {
+        struct S1;
+        struct S2;
+        thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell {
+            value: None
+        })
+        thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell {
+            value: None
+        })
+        static mut HITS: uint = 0;
+
+        impl Drop for S1 {
+            fn drop(&mut self) {
+                unsafe {
+                    HITS += 1;
+                    if K2.destroyed() {
+                        assert_eq!(HITS, 3);
+                    } else {
+                        if HITS == 1 {
+                            K2.with(|s| *s.get() = Some(S2));
+                        } else {
+                            assert_eq!(HITS, 3);
+                        }
+                    }
+                }
+            }
+        }
+        impl Drop for S2 {
+            fn drop(&mut self) {
+                unsafe {
+                    HITS += 1;
+                    assert!(!K1.destroyed());
+                    assert_eq!(HITS, 2);
+                    K1.with(|s| *s.get() = Some(S1));
+                }
+            }
+        }
+
+        Thread::start(proc() {
+            drop(S1);
+        }).join();
+    }
+
+    #[test]
+    fn self_referential() {
+        struct S1;
+        thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell {
+            value: None
+        })
+
+        impl Drop for S1 {
+            fn drop(&mut self) {
+                assert!(K1.destroyed());
+            }
+        }
+
+        Thread::start(proc() unsafe {
+            K1.with(|s| *s.get() = Some(S1));
+        }).join();
+    }
+
+    #[test]
+    fn dtors_in_dtors_in_dtors() {
+        struct S1(Sender<()>);
+        thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell {
+            value: None
+        })
+        thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell {
+            value: None
+        })
+
+        impl Drop for S1 {
+            fn drop(&mut self) {
+                let S1(ref tx) = *self;
+                unsafe {
+                    if !K2.destroyed() {
+                        K2.with(|s| *s.get() = Some(Foo(tx.clone())));
+                    }
+                }
+            }
+        }
+
+        let (tx, rx) = channel();
+        spawn(proc() unsafe {
+            let mut tx = Some(tx);
+            K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
+        });
+        rx.recv();
+    }
+}
+
+#[cfg(test)]
+mod dynamic_tests {
+    use prelude::*;
+
+    use cell::RefCell;
+    use collections::HashMap;
+
+    #[test]
+    fn smoke() {
+        fn square(i: int) -> int { i * i }
+        thread_local!(static FOO: int = square(3))
+
+        FOO.with(|f| {
+            assert_eq!(*f, 9);
+        });
+    }
+
+    #[test]
+    fn hashmap() {
+        fn map() -> RefCell<HashMap<int, int>> {
+            let mut m = HashMap::new();
+            m.insert(1, 2);
+            RefCell::new(m)
+        }
+        thread_local!(static FOO: RefCell<HashMap<int, int>> = map())
+
+        FOO.with(|map| {
+            assert_eq!(map.borrow()[1], 2);
+        });
+    }
+
+    #[test]
+    fn refcell_vec() {
+        thread_local!(static FOO: RefCell<Vec<uint>> = RefCell::new(vec![1, 2, 3]))
+
+        FOO.with(|vec| {
+            assert_eq!(vec.borrow().len(), 3);
+            vec.borrow_mut().push(4);
+            assert_eq!(vec.borrow()[3], 4);
+        });
+    }
+}
diff --git a/src/libstd/thread_local/scoped.rs b/src/libstd/thread_local/scoped.rs
new file mode 100644 (file)
index 0000000..11d539c
--- /dev/null
@@ -0,0 +1,261 @@
+// 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);
+            });
+        });
+    }
+}
+
index 42fdb50f87a898284a8d4ed1e0974e954fba4992..fdfa275549a2cc73a62fc5f9f19c6c2efb30a5ac 100644 (file)
 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)
 }
 
index 281bde3129aba4e642a2479e827bd57f1328f952..5f4e675aad5d5676fc4b3f8c5f7cc6a2b085a435 100644 (file)
 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,
index 2ddcab10cdafd7956888cdb465ae8fc0421a4b16..e6d886e28ba7fcb113e4b535a2fafbf812bc721a 100644 (file)
@@ -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.
index 4272b57a4dc5145f309f819802d4cf95929855ab..3a3407aedba9647ebc13677dddb614c75114bfef 100644 (file)
@@ -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
index 1592ffb6c6790b976f02901f91088a28717f3f88..021eae90cf8005a51c740910e7796638b28aa6e2 100644 (file)
@@ -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>));
 }
 
diff --git a/src/test/compile-fail/core-tls-store-pointer.rs b/src/test/compile-fail/core-tls-store-pointer.rs
deleted file mode 100644 (file)
index d77c552..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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() {}
index ec0e656cc292740d5294cc0a65b7d720a58daaf9..3f2ecd86abe89a9a382aed808a2e2f89e0248b3f 100644 (file)
 // 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
 }
diff --git a/src/test/run-pass/macro-local-data-key.rs b/src/test/run-pass/macro-local-data-key.rs
deleted file mode 100644 (file)
index 730b0b0..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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);
-}
diff --git a/src/test/run-pass/panic-during-tld-destroy.rs b/src/test/run-pass/panic-during-tld-destroy.rs
deleted file mode 100644 (file)
index 2f0d6cf..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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 std::task;
-
-static mut DROPS: uint = 0;
-
-struct Foo;
-impl Drop for Foo {
-    fn drop(&mut self) {
-        unsafe { DROPS += 1; }
-        panic!()
-    }
-}
-
-fn main() {
-    let _ = task::try(proc() {
-        local_data_key!(foo: Foo);
-        foo.replace(Some(Foo));
-    });
-
-    unsafe {
-        assert_eq!(DROPS, 1);
-    }
-}
-
index ed4c20c80943ab58ca9a98ef0683e8dfc7a3e54a..683f7038ead7ac237be7356fa904e8c7e8d67ce6 100644 (file)
@@ -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) {