Skip to content

Commit 9e4bdff

Browse files
committed
---
yaml --- r: 64372 b: refs/heads/snap-stage3 c: 948a624 h: refs/heads/master v: v3
1 parent 996dbe0 commit 9e4bdff

File tree

4 files changed

+154
-69
lines changed

4 files changed

+154
-69
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
refs/heads/master: 2d28d645422c1617be58c8ca7ad9a457264ca850
33
refs/heads/snap-stage1: e33de59e47c5076a89eadeb38f4934f58a3618a6
4-
refs/heads/snap-stage3: d5f53c780eb1d67749cf47cb552237509ff87ca4
4+
refs/heads/snap-stage3: 948a62401ef717eb484c2215713291749f0f35ed
55
refs/heads/try: 7b78b52e602bb3ea8174f9b2006bff3315f03ef9
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
77
refs/heads/ndm: f3868061cd7988080c30d6d5bf352a5a5fe2460b

branches/snap-stage3/src/libextra/test.rs

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818

1919
use getopts;
20-
use getopts::groups;
2120
use json::ToJson;
2221
use json;
2322
use serialize::Decodable;
@@ -29,7 +28,6 @@ use time::precise_time_ns;
2928
use treemap::TreeMap;
3029

3130
use std::comm::{stream, SharedChan};
32-
use std::libc;
3331
use std::either;
3432
use std::io;
3533
use std::result;
@@ -158,64 +156,22 @@ pub struct TestOpts {
158156

159157
type OptRes = Either<TestOpts, ~str>;
160158

161-
fn optgroups() -> ~[getopts::groups::OptGroup] {
162-
~[groups::optflag("", "ignored", "Run ignored tests"),
163-
groups::optflag("", "test", "Run tests and not benchmarks"),
164-
groups::optflag("", "bench", "Run benchmarks instead of tests"),
165-
groups::optflag("h", "help", "Display this message (longer with --help)"),
166-
groups::optopt("", "save-metrics", "Location to save bench metrics",
167-
"PATH"),
168-
groups::optopt("", "ratchet-metrics",
169-
"Location to load and save metrics from. The metrics \
170-
loaded are cause benchmarks to fail if they run too \
171-
slowly", "PATH"),
172-
groups::optopt("", "ratchet-noise-percent",
173-
"Tests within N% of the recorded metrics will be \
174-
considered as passing", "PERCENTAGE"),
175-
groups::optopt("", "logfile", "Write logs to the specified file instead \
176-
of stdout", "PATH")]
177-
}
178-
179-
fn usage(binary: &str, helpstr: &str) -> ! {
180-
let message = fmt!("Usage: %s [OPTIONS] [FILTER]", binary);
181-
println(groups::usage(message, optgroups()));
182-
if helpstr == "help" {
183-
println("\
184-
The FILTER is matched against the name of all tests to run, and if any tests
185-
have a substring match, only those tests are run.
186-
187-
By default, all tests are run in parallel. This can be altered with the
188-
RUST_THREADS environment variable when running tests (set it to 1).
189-
190-
Test Attributes:
191-
192-
#[test] - Indicates a function is a test to be run. This function
193-
takes no arguments.
194-
#[bench] - Indicates a function is a benchmark to be run. This
195-
function takes one argument (extra::test::BenchHarness).
196-
#[should_fail] - This function (also labeled with #[test]) will only pass if
197-
the code causes a failure (an assertion failure or fail!)
198-
#[ignore] - When applied to a function which is already attributed as a
199-
test, then the test runner will ignore these tests during
200-
normal test runs. Running with --ignored will run these
201-
tests. This may also be written as #[ignore(cfg(...))] to
202-
ignore the test on certain configurations.");
203-
}
204-
unsafe { libc::exit(0) }
205-
}
206-
207159
// Parses command line arguments into test options
208160
pub fn parse_opts(args: &[~str]) -> OptRes {
209161
let args_ = args.tail();
162+
let opts = ~[getopts::optflag("ignored"),
163+
getopts::optflag("test"),
164+
getopts::optflag("bench"),
165+
getopts::optopt("save-metrics"),
166+
getopts::optopt("ratchet-metrics"),
167+
getopts::optopt("ratchet-noise-percent"),
168+
getopts::optopt("logfile")];
210169
let matches =
211-
match groups::getopts(args_, optgroups()) {
170+
match getopts::getopts(args_, opts) {
212171
Ok(m) => m,
213172
Err(f) => return either::Right(getopts::fail_str(f))
214173
};
215174

216-
if getopts::opt_present(&matches, "h") { usage(args[0], "h"); }
217-
if getopts::opt_present(&matches, "help") { usage(args[0], "help"); }
218-
219175
let filter =
220176
if matches.free.len() > 0 {
221177
Some(copy (matches).free[0])

branches/snap-stage3/src/libstd/local_data.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ magic.
4141

4242
use prelude::*;
4343

44-
use task::local_data_priv::{local_get, local_pop, local_set, Handle};
44+
use task::local_data_priv::*;
4545

4646
#[cfg(test)] use task;
4747

@@ -95,6 +95,13 @@ pub fn get<T: 'static, U>(key: Key<@T>, f: &fn(Option<&@T>) -> U) -> U {
9595
pub fn get<T: 'static, U>(key: Key<T>, f: &fn(Option<&T>) -> U) -> U {
9696
unsafe { local_get(Handle::new(), key, f) }
9797
}
98+
/**
99+
* Retrieve a mutable borrowed pointer to a task-local data value.
100+
*/
101+
#[cfg(not(stage0))]
102+
pub fn get_mut<T: 'static, U>(key: Key<T>, f: &fn(Option<&mut T>) -> U) -> U {
103+
unsafe { local_get_mut(Handle::new(), key, f) }
104+
}
98105
/**
99106
* Store a value in task-local data. If this key already has a value,
100107
* that value is overwritten (and its destructor is run).
@@ -262,6 +269,34 @@ fn test_static_pointer() {
262269
fn test_owned() {
263270
static key: Key<~int> = &Key;
264271
set(key, ~1);
272+
273+
do get(key) |v| {
274+
do get(key) |v| {
275+
do get(key) |v| {
276+
assert_eq!(**v.unwrap(), 1);
277+
}
278+
assert_eq!(**v.unwrap(), 1);
279+
}
280+
assert_eq!(**v.unwrap(), 1);
281+
}
282+
set(key, ~2);
283+
do get(key) |v| {
284+
assert_eq!(**v.unwrap(), 2);
285+
}
286+
}
287+
288+
#[test]
289+
fn test_get_mut() {
290+
static key: Key<int> = &Key;
291+
set(key, 1);
292+
293+
do get_mut(key) |v| {
294+
*v.unwrap() = 2;
295+
}
296+
297+
do get(key) |v| {
298+
assert_eq!(*v.unwrap(), 2);
299+
}
265300
}
266301

267302
#[test]
@@ -283,3 +318,43 @@ fn test_same_key_type() {
283318
get(key4, |x| assert_eq!(*x.unwrap(), 4));
284319
get(key5, |x| assert_eq!(*x.unwrap(), 5));
285320
}
321+
322+
#[test]
323+
#[should_fail]
324+
fn test_nested_get_set1() {
325+
static key: Key<int> = &Key;
326+
set(key, 4);
327+
do get(key) |_| {
328+
set(key, 4);
329+
}
330+
}
331+
332+
#[test]
333+
#[should_fail]
334+
fn test_nested_get_mut2() {
335+
static key: Key<int> = &Key;
336+
set(key, 4);
337+
do get(key) |_| {
338+
get_mut(key, |_| {})
339+
}
340+
}
341+
342+
#[test]
343+
#[should_fail]
344+
fn test_nested_get_mut3() {
345+
static key: Key<int> = &Key;
346+
set(key, 4);
347+
do get_mut(key) |_| {
348+
get(key, |_| {})
349+
}
350+
}
351+
352+
#[test]
353+
#[should_fail]
354+
fn test_nested_get_mut4() {
355+
static key: Key<int> = &Key;
356+
set(key, 4);
357+
do get_mut(key) |_| {
358+
get_mut(key, |_| {})
359+
}
360+
}

branches/snap-stage3/src/libstd/task/local_data_priv.rs

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,21 @@ impl Handle {
4444
}
4545
}
4646

47+
#[deriving(Eq)]
48+
enum LoanState {
49+
NoLoan, ImmLoan, MutLoan
50+
}
51+
52+
impl LoanState {
53+
fn describe(&self) -> &'static str {
54+
match *self {
55+
NoLoan => "no loan",
56+
ImmLoan => "immutable",
57+
MutLoan => "mutable"
58+
}
59+
}
60+
}
61+
4762
trait LocalData {}
4863
impl<T: 'static> LocalData for T {}
4964

@@ -77,7 +92,7 @@ impl<T: 'static> LocalData for T {}
7792
//
7893
// n.b. If TLS is used heavily in future, this could be made more efficient with
7994
// a proper map.
80-
type TaskLocalMap = ~[Option<(*libc::c_void, TLSValue, uint)>];
95+
type TaskLocalMap = ~[Option<(*libc::c_void, TLSValue, LoanState)>];
8196
type TLSValue = ~LocalData:;
8297

8398
fn cleanup_task_local_map(map_ptr: *libc::c_void) {
@@ -152,9 +167,10 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
152167

153168
for map.mut_iter().advance |entry| {
154169
match *entry {
155-
Some((k, _, loans)) if k == key_value => {
156-
if loans != 0 {
157-
fail!("TLS value has been loaned via get already");
170+
Some((k, _, loan)) if k == key_value => {
171+
if loan != NoLoan {
172+
fail!("TLS value cannot be removed because it is already \
173+
borrowed as %s", loan.describe());
158174
}
159175
// Move the data out of the `entry` slot via util::replace. This
160176
// is guaranteed to succeed because we already matched on `Some`
@@ -192,6 +208,29 @@ pub unsafe fn local_pop<T: 'static>(handle: Handle,
192208
pub unsafe fn local_get<T: 'static, U>(handle: Handle,
193209
key: local_data::Key<T>,
194210
f: &fn(Option<&T>) -> U) -> U {
211+
local_get_with(handle, key, ImmLoan, f)
212+
}
213+
214+
pub unsafe fn local_get_mut<T: 'static, U>(handle: Handle,
215+
key: local_data::Key<T>,
216+
f: &fn(Option<&mut T>) -> U) -> U {
217+
do local_get_with(handle, key, MutLoan) |x| {
218+
match x {
219+
None => f(None),
220+
// We're violating a lot of compiler guarantees with this
221+
// invocation of `transmute_mut`, but we're doing runtime checks to
222+
// ensure that it's always valid (only one at a time).
223+
//
224+
// there is no need to be upset!
225+
Some(x) => { f(Some(cast::transmute_mut(x))) }
226+
}
227+
}
228+
}
229+
230+
unsafe fn local_get_with<T: 'static, U>(handle: Handle,
231+
key: local_data::Key<T>,
232+
state: LoanState,
233+
f: &fn(Option<&T>) -> U) -> U {
195234
// This function must be extremely careful. Because TLS can store owned
196235
// values, and we must have some form of `get` function other than `pop`,
197236
// this function has to give a `&` reference back to the caller.
@@ -218,12 +257,24 @@ pub unsafe fn local_get<T: 'static, U>(handle: Handle,
218257
None => { return f(None); }
219258
Some(i) => {
220259
let ret;
260+
let mut return_loan = false;
221261
match map[i] {
222-
Some((_, ref data, ref mut loans)) => {
223-
*loans = *loans + 1;
262+
Some((_, ref data, ref mut loan)) => {
263+
match (state, *loan) {
264+
(_, NoLoan) => {
265+
*loan = state;
266+
return_loan = true;
267+
}
268+
(ImmLoan, ImmLoan) => {}
269+
(want, cur) => {
270+
fail!("TLS slot cannot be borrowed as %s because \
271+
it is already borrowed as %s",
272+
want.describe(), cur.describe());
273+
}
274+
}
224275
// data was created with `~~T as ~LocalData`, so we extract
225276
// pointer part of the trait, (as ~~T), and then use
226-
// compiler coercions to achieve a '&' pointer
277+
// compiler coercions to achieve a '&' pointer.
227278
match *cast::transmute::<&TLSValue, &(uint, ~~T)>(data) {
228279
(_vtable, ref box) => {
229280
let value: &T = **box;
@@ -238,9 +289,11 @@ pub unsafe fn local_get<T: 'static, U>(handle: Handle,
238289
// 'f' returned because `f` could have appended more TLS items which
239290
// in turn relocated the vector. Hence we do another lookup here to
240291
// fixup the loans.
241-
match map[i] {
242-
Some((_, _, ref mut loans)) => { *loans = *loans - 1; }
243-
None => { libc::abort(); }
292+
if return_loan {
293+
match map[i] {
294+
Some((_, _, ref mut loan)) => { *loan = NoLoan; }
295+
None => { libc::abort(); }
296+
}
244297
}
245298
return ret;
246299
}
@@ -269,9 +322,10 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
269322
// First see if the map contains this key already
270323
let curspot = map.iter().position(|entry| {
271324
match *entry {
272-
Some((ekey, _, loans)) if key == ekey => {
273-
if loans != 0 {
274-
fail!("TLS value has been loaned via get already");
325+
Some((ekey, _, loan)) if key == ekey => {
326+
if loan != NoLoan {
327+
fail!("TLS value cannot be overwritten because it is
328+
already borrowed as %s", loan.describe())
275329
}
276330
true
277331
}
@@ -286,7 +340,7 @@ pub unsafe fn local_set<T: 'static>(handle: Handle,
286340
}
287341

288342
match insertion_position(map, keyval) {
289-
Some(i) => { map[i] = Some((keyval, data, 0)); }
290-
None => { map.push(Some((keyval, data, 0))); }
343+
Some(i) => { map[i] = Some((keyval, data, NoLoan)); }
344+
None => { map.push(Some((keyval, data, NoLoan))); }
291345
}
292346
}

0 commit comments

Comments
 (0)