Skip to content

Commit

Permalink
std: Add a new top-level thread_local module
Browse files Browse the repository at this point in the history
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]: rust-lang/rfcs#461
[breaking-change]
  • Loading branch information
alexcrichton committed Nov 24, 2014
1 parent 4e52595 commit a9c1152
Show file tree
Hide file tree
Showing 38 changed files with 1,809 additions and 1,150 deletions.
19 changes: 14 additions & 5 deletions src/liblog/lib.rs
Expand Up @@ -171,7 +171,7 @@

extern crate regex;

use regex::Regex;
use std::cell::RefCell;
use std::fmt;
use std::io::LineBufferedWriter;
use std::io;
Expand All @@ -181,6 +181,8 @@ use std::rt;
use std::slice;
use std::sync::{Once, ONCE_INIT};

use regex::Regex;

use directive::LOG_LEVEL_NAMES;

pub mod macros;
Expand Down Expand Up @@ -213,7 +215,9 @@ pub const WARN: u32 = 2;
/// 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
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
Expand Down
13 changes: 8 additions & 5 deletions src/librustc/util/common.rs
Expand Up @@ -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};
Expand All @@ -26,11 +26,14 @@ use syntax::visit::Visitor;
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;
Expand All @@ -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
}
Expand Down
35 changes: 21 additions & 14 deletions src/librustc_trans/trans/base.rs
Expand Up @@ -100,17 +100,20 @@ use syntax::visit::Visitor;
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 {
Expand All @@ -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: () }
}

Expand Down
26 changes: 12 additions & 14 deletions src/librustdoc/html/format.rs
Expand Up @@ -26,7 +26,7 @@ use stability_summary::ModuleSummary;
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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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()));
Expand All @@ -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,
};
Expand Down Expand Up @@ -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));
Expand Down
55 changes: 31 additions & 24 deletions src/librustdoc/html/markdown.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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());
}
})
}
}

Expand All @@ -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) => {
Expand All @@ -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);
Expand Down Expand Up @@ -418,8 +425,8 @@ impl 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> {
Expand Down

5 comments on commit a9c1152

@bors
Copy link
Contributor

@bors bors commented on a9c1152 Nov 24, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from aturon
at alexcrichton@a9c1152

@bors
Copy link
Contributor

@bors bors commented on a9c1152 Nov 24, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging alexcrichton/rust/rm-std-local-data = a9c1152 into auto

@bors
Copy link
Contributor

@bors bors commented on a9c1152 Nov 24, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alexcrichton/rust/rm-std-local-data = a9c1152 merged ok, testing candidate = bad1062

@bors
Copy link
Contributor

@bors bors commented on a9c1152 Nov 24, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on a9c1152 Nov 24, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = bad1062

Please sign in to comment.