|
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