| 
8 | 8 | // option. This file may not be copied, modified, or distributed  | 
9 | 9 | // except according to those terms.  | 
10 | 10 | 
 
  | 
 | 11 | +use std::cell::Cell;  | 
11 | 12 | use std::cmp::Ordering::{Equal, Greater, Less};  | 
 | 13 | +use std::cmp::Ordering;  | 
12 | 14 | use std::mem;  | 
 | 15 | +use std::panic;  | 
13 | 16 | use std::rc::Rc;  | 
 | 17 | +use std::sync::atomic::Ordering::Relaxed;  | 
 | 18 | +use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize};  | 
 | 19 | +use std::thread;  | 
14 | 20 | 
 
  | 
15 | 21 | use rand::{Rng, thread_rng};  | 
16 | 22 | 
 
  | 
@@ -1341,3 +1347,162 @@ fn test_copy_from_slice_dst_shorter() {  | 
1341 | 1347 |     let mut dst = [0; 3];  | 
1342 | 1348 |     dst.copy_from_slice(&src);  | 
1343 | 1349 | }  | 
 | 1350 | + | 
 | 1351 | +const MAX_LEN: usize = 80;  | 
 | 1352 | + | 
 | 1353 | +static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [  | 
 | 1354 | +    // FIXME #5244: AtomicUsize is not Copy.  | 
 | 1355 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1356 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1357 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1358 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1359 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1360 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1361 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1362 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1363 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1364 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1365 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1366 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1367 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1368 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1369 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1370 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1371 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1372 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1373 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1374 | +    AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),  | 
 | 1375 | +];  | 
 | 1376 | + | 
 | 1377 | +static VERSIONS: AtomicUsize = ATOMIC_USIZE_INIT;  | 
 | 1378 | + | 
 | 1379 | +#[derive(Clone, Eq)]  | 
 | 1380 | +struct DropCounter {  | 
 | 1381 | +    x: u32,  | 
 | 1382 | +    id: usize,  | 
 | 1383 | +    version: Cell<usize>,  | 
 | 1384 | +}  | 
 | 1385 | + | 
 | 1386 | +impl PartialEq for DropCounter {  | 
 | 1387 | +    fn eq(&self, other: &Self) -> bool {  | 
 | 1388 | +        self.partial_cmp(other) == Some(Ordering::Equal)  | 
 | 1389 | +    }  | 
 | 1390 | +}  | 
 | 1391 | + | 
 | 1392 | +impl PartialOrd for DropCounter {  | 
 | 1393 | +    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {  | 
 | 1394 | +        self.version.set(self.version.get() + 1);  | 
 | 1395 | +        other.version.set(other.version.get() + 1);  | 
 | 1396 | +        VERSIONS.fetch_add(2, Relaxed);  | 
 | 1397 | +        self.x.partial_cmp(&other.x)  | 
 | 1398 | +    }  | 
 | 1399 | +}  | 
 | 1400 | + | 
 | 1401 | +impl Ord for DropCounter {  | 
 | 1402 | +    fn cmp(&self, other: &Self) -> Ordering {  | 
 | 1403 | +        self.partial_cmp(other).unwrap()  | 
 | 1404 | +    }  | 
 | 1405 | +}  | 
 | 1406 | + | 
 | 1407 | +impl Drop for DropCounter {  | 
 | 1408 | +    fn drop(&mut self) {  | 
 | 1409 | +        DROP_COUNTS[self.id].fetch_add(1, Relaxed);  | 
 | 1410 | +        VERSIONS.fetch_sub(self.version.get(), Relaxed);  | 
 | 1411 | +    }  | 
 | 1412 | +}  | 
 | 1413 | + | 
 | 1414 | +macro_rules! test {  | 
 | 1415 | +    ($input:ident, $func:ident) => {  | 
 | 1416 | +        let len = $input.len();  | 
 | 1417 | + | 
 | 1418 | +        // Work out the total number of comparisons required to sort  | 
 | 1419 | +        // this array...  | 
 | 1420 | +        let mut count = 0usize;  | 
 | 1421 | +        $input.to_owned().$func(|a, b| { count += 1; a.cmp(b) });  | 
 | 1422 | + | 
 | 1423 | +        // ... and then panic on each and every single one.  | 
 | 1424 | +        for panic_countdown in 0..count {  | 
 | 1425 | +            // Refresh the counters.  | 
 | 1426 | +            VERSIONS.store(0, Relaxed);  | 
 | 1427 | +            for i in 0..len {  | 
 | 1428 | +                DROP_COUNTS[i].store(0, Relaxed);  | 
 | 1429 | +            }  | 
 | 1430 | + | 
 | 1431 | +            let v = $input.to_owned();  | 
 | 1432 | +            let _ = thread::spawn(move || {  | 
 | 1433 | +                let mut v = v;  | 
 | 1434 | +                let mut panic_countdown = panic_countdown;  | 
 | 1435 | +                v.$func(|a, b| {  | 
 | 1436 | +                    if panic_countdown == 0 {  | 
 | 1437 | +                        SILENCE_PANIC.with(|s| s.set(true));  | 
 | 1438 | +                        panic!();  | 
 | 1439 | +                    }  | 
 | 1440 | +                    panic_countdown -= 1;  | 
 | 1441 | +                    a.cmp(b)  | 
 | 1442 | +                })  | 
 | 1443 | +            }).join();  | 
 | 1444 | + | 
 | 1445 | +            // Check that the number of things dropped is exactly  | 
 | 1446 | +            // what we expect (i.e. the contents of `v`).  | 
 | 1447 | +            for (i, c) in DROP_COUNTS.iter().enumerate().take(len) {  | 
 | 1448 | +                let count = c.load(Relaxed);  | 
 | 1449 | +                assert!(count == 1,  | 
 | 1450 | +                        "found drop count == {} for i == {}, len == {}",  | 
 | 1451 | +                        count, i, len);  | 
 | 1452 | +            }  | 
 | 1453 | + | 
 | 1454 | +            // Check that the most recent versions of values were dropped.  | 
 | 1455 | +            assert_eq!(VERSIONS.load(Relaxed), 0);  | 
 | 1456 | +        }  | 
 | 1457 | +    }  | 
 | 1458 | +}  | 
 | 1459 | + | 
 | 1460 | +thread_local!(static SILENCE_PANIC: Cell<bool> = Cell::new(false));  | 
 | 1461 | + | 
 | 1462 | +#[test]  | 
 | 1463 | +#[cfg_attr(target_os = "emscripten", ignore)] // no threads  | 
 | 1464 | +fn panic_safe() {  | 
 | 1465 | +    let prev = panic::take_hook();  | 
 | 1466 | +    panic::set_hook(Box::new(move |info| {  | 
 | 1467 | +        if !SILENCE_PANIC.with(|s| s.get()) {  | 
 | 1468 | +            prev(info);  | 
 | 1469 | +        }  | 
 | 1470 | +    }));  | 
 | 1471 | + | 
 | 1472 | +    let mut rng = thread_rng();  | 
 | 1473 | + | 
 | 1474 | +    for len in (1..20).chain(70..MAX_LEN) {  | 
 | 1475 | +        for &modulus in &[5, 20, 50] {  | 
 | 1476 | +            for &has_runs in &[false, true] {  | 
 | 1477 | +                let mut input = (0..len)  | 
 | 1478 | +                    .map(|id| {  | 
 | 1479 | +                        DropCounter {  | 
 | 1480 | +                            x: rng.next_u32() % modulus,  | 
 | 1481 | +                            id: id,  | 
 | 1482 | +                            version: Cell::new(0),  | 
 | 1483 | +                        }  | 
 | 1484 | +                    })  | 
 | 1485 | +                    .collect::<Vec<_>>();  | 
 | 1486 | + | 
 | 1487 | +                if has_runs {  | 
 | 1488 | +                    for c in &mut input {  | 
 | 1489 | +                        c.x = c.id as u32;  | 
 | 1490 | +                    }  | 
 | 1491 | + | 
 | 1492 | +                    for _ in 0..5 {  | 
 | 1493 | +                        let a = rng.gen::<usize>() % len;  | 
 | 1494 | +                        let b = rng.gen::<usize>() % len;  | 
 | 1495 | +                        if a < b {  | 
 | 1496 | +                            input[a..b].reverse();  | 
 | 1497 | +                        } else {  | 
 | 1498 | +                            input.swap(a, b);  | 
 | 1499 | +                        }  | 
 | 1500 | +                    }  | 
 | 1501 | +                }  | 
 | 1502 | + | 
 | 1503 | +                test!(input, sort_by);  | 
 | 1504 | +                test!(input, sort_unstable_by);  | 
 | 1505 | +            }  | 
 | 1506 | +        }  | 
 | 1507 | +    }  | 
 | 1508 | +}  | 
0 commit comments