Bug 1424081 - Update parking_lot_core Rust crate to 0.2.7 r=SimonSapin

MozReview-Commit-ID: G8C94Vt2RVx

--HG--
extra : rebase_source : e13d4b40761171a16fd99d29fccabe5c5d942d58
This commit is contained in:
Matt Brubeck 2017-12-07 14:37:31 -08:00
parent 53c3b17b09
commit 7f726d3cbb
21 changed files with 613 additions and 2060 deletions

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"0fbc0225f4a4220fa4e6390f5fd889f1739ae67fd6144baf8698f601f65632e4","src/lib.rs":"76b3e0b8c7c5931e71f2f563ca629b3b1a668a195ea454104537e39c4a43d8da","src/parking_lot.rs":"b451509ab016630edb5d17aafd6b0de1011663e2fe6e1b8c71361fe6f877fc98","src/spinwait.rs":"682e5a3fc71d1d7a7d8da377d1d93415fa64b4f53809bd80bbf2942628e3a23d","src/stable.rs":"cc18c58404dc6017924d88fb9f4ed94e5320b8cb0a36985162b23130b8cd7480","src/thread_parker/generic.rs":"041c56505851c0f2f68f250cb74851a198ea15e86d0b4fc2f8065fd024b9dc7b","src/thread_parker/linux.rs":"0a3c63a8cba192827edb55dd785ccab22229370ad2bc4b52e81cc25c1fb63696","src/thread_parker/unix.rs":"64d18bf39ebe13e08a43d641ec3c9e66048663b4fd0bae1af555375cf735f7fd","src/thread_parker/windows/keyed_event.rs":"ceea3746ae04f59f86a6ccddde651cbbf8a203eb53a9e83349dfb5159eca4d88","src/thread_parker/windows/mod.rs":"d0caa762effeba24a5d866be812d8b1e8e076394142cdde5ef873a1247e867d9","src/thread_parker/windows/waitaddress.rs":"7f280d3c60bc4290b5bf30bc9740322cc0723742cfce58dcfcbfe4058e32d3a0","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"c33355107175d58f16fc6814cbd7792d2b9d92deae8311baf4306542c35c9853"},"package":"a25dd36576d01cca115881dc920b1f0dd0037303ed8cfa0c5d0a4966151757f7"}
{"files":{"Cargo.toml":"c8a7070b6801c4cc9f410d45819dab3c6efcbe77806a617415289d8de0fbb01b","src/lib.rs":"932b67d85b4176bfb1d7a78b70ec5dd356839158dad22d2e932b88be30925572","src/parking_lot.rs":"aabffbdf465648a1066b24e88c74fbff9cef636354e0bfdd125937f61d771449","src/spinwait.rs":"5aee4a6e8d33eec1b6a81b21ff3b223460d8fa2d37e633b31b8ca27fabe659cb","src/stable.rs":"4562ea9a408bd3917df6d30c8354bf51f46bc69b3360815813730743204adfdc","src/thread_parker/generic.rs":"0c30db3d1c96bd5ef284a4761a829aba8d21fc813b3d1d70b2baf5f00744e006","src/thread_parker/linux.rs":"4e0a142ce3ff59d37e5c452bf57b3481ef00274e8e489ac1a1d11b2f31b473ed","src/thread_parker/unix.rs":"ff5a543f21895c8114bd4f89b5764882beab1f3a37ddbd8fc31c783e1db3f1c1","src/thread_parker/windows/keyed_event.rs":"b54b0855b10ed2c188ce42094c6e4069e92e325f870d0c0f8244bfe2d7811b66","src/thread_parker/windows/mod.rs":"dc5359b10275a4aaee04024c202b115d267e4ea15917546b042c4035c0218136","src/thread_parker/windows/waitaddress.rs":"2da78bfe09e4262a6cd6271d6416a9debdb3fd3abb1993be1c68515952576874","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"6ab156a775c46423bbb7dae520f181dde1747140d52ba995850969498559c7b2"},"package":"6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"}

View File

@ -12,25 +12,38 @@
[package]
name = "parking_lot_core"
version = "0.2.4"
version = "0.2.7"
authors = ["Amanieu d'Antras <amanieu@gmail.com>"]
description = "An advanced API for creating custom synchronization primitives."
documentation = "https://amanieu.github.io/parking_lot/parking_lot_core/index.html"
keywords = ["mutex", "condvar", "rwlock", "once", "thread"]
license = "Apache-2.0/MIT"
repository = "https://github.com/Amanieu/parking_lot"
[dependencies.backtrace]
version = "0.3.2"
optional = true
[dependencies.petgraph]
version = "0.4.5"
optional = true
[dependencies.rand]
version = "0.3"
[dependencies.smallvec]
version = "0.4"
version = "0.6"
[dependencies.thread-id]
version = "3.2.0"
optional = true
[features]
deadlock_detection = ["petgraph", "thread-id", "backtrace"]
nightly = []
[target."cfg(unix)".dependencies.libc]
version = "0.2.15"
[target."cfg(windows)".dependencies.winapi]
version = "0.2"
[target."cfg(windows)".dependencies.kernel32-sys]
version = "0.2"
[target."cfg(windows)".dependencies.winapi]
version = "0.2"

View File

@ -41,16 +41,23 @@
#![cfg_attr(all(feature = "nightly", target_os = "linux"), feature(integer_atomics))]
#![cfg_attr(feature = "nightly", feature(asm))]
extern crate smallvec;
extern crate rand;
extern crate smallvec;
#[cfg(feature = "deadlock_detection")]
extern crate backtrace;
#[cfg(feature = "deadlock_detection")]
extern crate petgraph;
#[cfg(feature = "deadlock_detection")]
extern crate thread_id;
#[cfg(unix)]
extern crate libc;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate kernel32;
#[cfg(windows)]
extern crate winapi;
#[cfg(all(feature = "nightly", target_os = "linux"))]
#[path = "thread_parker/linux.rs"]
@ -73,7 +80,8 @@ mod spinwait;
mod word_lock;
mod parking_lot;
pub use parking_lot::{ParkResult, UnparkResult, RequeueOp, UnparkToken, ParkToken, FilterOp};
pub use parking_lot::{DEFAULT_UNPARK_TOKEN, DEFAULT_PARK_TOKEN};
pub use parking_lot::{park, unpark_one, unpark_all, unpark_requeue, unpark_filter};
pub use parking_lot::{FilterOp, ParkResult, ParkToken, RequeueOp, UnparkResult, UnparkToken};
pub use parking_lot::{DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN};
pub use parking_lot::{park, unpark_all, unpark_filter, unpark_one, unpark_requeue};
pub use spinwait::SpinWait;
pub use parking_lot::deadlock;

View File

@ -6,10 +6,10 @@
// copied, modified, or distributed except according to those terms.
#[cfg(feature = "nightly")]
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(not(feature = "nightly"))]
use stable::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::time::{Instant, Duration};
use stable::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::time::{Duration, Instant};
use std::cell::{Cell, UnsafeCell};
use std::ptr;
use std::mem;
@ -17,7 +17,7 @@ use std::thread::LocalKey;
#[cfg(not(feature = "nightly"))]
use std::panic;
use smallvec::SmallVec;
use rand::{self, XorShiftRng, Rng};
use rand::{self, Rng, XorShiftRng};
use thread_parker::ThreadParker;
use word_lock::WordLock;
use util::UncheckedOptionExt;
@ -132,6 +132,13 @@ struct ThreadData {
// ParkToken value set by the thread when it was parked
park_token: Cell<ParkToken>,
// Is the thread parked with a timeout?
parked_with_timeout: Cell<bool>,
// Extra data for deadlock detection
// TODO: once supported in stable replace with #[cfg...] & remove dummy struct/impl
#[allow(dead_code)] deadlock_data: deadlock::DeadlockData,
}
impl ThreadData {
@ -149,6 +156,8 @@ impl ThreadData {
next_in_queue: Cell::new(ptr::null()),
unpark_token: Cell::new(DEFAULT_UNPARK_TOKEN),
park_token: Cell::new(DEFAULT_PARK_TOKEN),
parked_with_timeout: Cell::new(false),
deadlock_data: deadlock::DeadlockData::new(),
}
}
}
@ -194,10 +203,12 @@ unsafe fn get_hashtable() -> *const HashTable {
// If this fails then it means some other thread created the hash
// table first.
match HASHTABLE.compare_exchange(0,
new_table as usize,
Ordering::Release,
Ordering::Relaxed) {
match HASHTABLE.compare_exchange(
0,
new_table as usize,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => return new_table,
Err(x) => table = x,
}
@ -219,8 +230,10 @@ unsafe fn grow_hashtable(num_threads: usize) {
// If this fails then it means some other thread created the hash
// table first.
if HASHTABLE.compare_exchange(0, new_table as usize, Ordering::Release, Ordering::Relaxed)
.is_ok() {
if HASHTABLE
.compare_exchange(0, new_table as usize, Ordering::Release, Ordering::Relaxed)
.is_ok()
{
return;
}
@ -267,7 +280,9 @@ unsafe fn grow_hashtable(num_threads: usize) {
if new_table.entries[hash].queue_tail.get().is_null() {
new_table.entries[hash].queue_head.set(current);
} else {
(*new_table.entries[hash].queue_tail.get()).next_in_queue.set(current);
(*new_table.entries[hash].queue_tail.get())
.next_in_queue
.set(current);
}
new_table.entries[hash].queue_tail.set(current);
(*current).next_in_queue.set(ptr::null());
@ -336,8 +351,9 @@ unsafe fn lock_bucket_checked<'a>(key: &AtomicUsize) -> (usize, &'a Bucket) {
// Check that both the hash table and key are correct while the bucket
// is locked. Note that the key can't change once we locked the proper
// bucket for it, so we just keep trying until we have the correct key.
if HASHTABLE.load(Ordering::Relaxed) == hashtable as usize &&
key.load(Ordering::Relaxed) == current_key {
if HASHTABLE.load(Ordering::Relaxed) == hashtable as usize
&& key.load(Ordering::Relaxed) == current_key
{
return (current_key, bucket);
}
@ -508,36 +524,41 @@ pub const DEFAULT_PARK_TOKEN: ParkToken = ParkToken(0);
/// to call `unpark_one`, `unpark_all`, `unpark_requeue` or `unpark_filter`, but
/// it is not allowed to call `park` or panic.
#[inline]
pub unsafe fn park<V, B, T>(key: usize,
validate: V,
before_sleep: B,
timed_out: T,
park_token: ParkToken,
timeout: Option<Instant>)
-> ParkResult
where V: FnOnce() -> bool,
B: FnOnce(),
T: FnOnce(usize, bool)
pub unsafe fn park<V, B, T>(
key: usize,
validate: V,
before_sleep: B,
timed_out: T,
park_token: ParkToken,
timeout: Option<Instant>,
) -> ParkResult
where
V: FnOnce() -> bool,
B: FnOnce(),
T: FnOnce(usize, bool),
{
let mut v = Some(validate);
let mut b = Some(before_sleep);
let mut t = Some(timed_out);
park_internal(key,
&mut || v.take().unchecked_unwrap()(),
&mut || b.take().unchecked_unwrap()(),
&mut |key, was_last_thread| t.take().unchecked_unwrap()(key, was_last_thread),
park_token,
timeout)
park_internal(
key,
&mut || v.take().unchecked_unwrap()(),
&mut || b.take().unchecked_unwrap()(),
&mut |key, was_last_thread| t.take().unchecked_unwrap()(key, was_last_thread),
park_token,
timeout,
)
}
// Non-generic version to reduce monomorphization cost
unsafe fn park_internal(key: usize,
validate: &mut FnMut() -> bool,
before_sleep: &mut FnMut(),
timed_out: &mut FnMut(usize, bool),
park_token: ParkToken,
timeout: Option<Instant>)
-> ParkResult {
unsafe fn park_internal(
key: usize,
validate: &mut FnMut() -> bool,
before_sleep: &mut FnMut(),
timed_out: &mut FnMut(usize, bool),
park_token: ParkToken,
timeout: Option<Instant>,
) -> ParkResult {
// Grab our thread data, this also ensures that the hash table exists
let mut thread_data = None;
let thread_data = get_thread_data(&mut thread_data);
@ -552,6 +573,7 @@ unsafe fn park_internal(key: usize,
}
// Append our thread data to the queue and unlock the bucket
thread_data.parked_with_timeout.set(timeout.is_some());
thread_data.next_in_queue.set(ptr::null());
thread_data.key.store(key, Ordering::Relaxed);
thread_data.park_token.set(park_token);
@ -574,6 +596,8 @@ unsafe fn park_internal(key: usize,
Some(timeout) => thread_data.parker.park_until(timeout),
None => {
thread_data.parker.park();
// call deadlock detection on_unpark hook
deadlock::on_unpark(thread_data);
true
}
};
@ -659,16 +683,18 @@ unsafe fn park_internal(key: usize,
/// panic or call into any function in `parking_lot`.
#[inline]
pub unsafe fn unpark_one<C>(key: usize, callback: C) -> UnparkResult
where C: FnOnce(UnparkResult) -> UnparkToken
where
C: FnOnce(UnparkResult) -> UnparkToken,
{
let mut c = Some(callback);
unpark_one_internal(key, &mut |result| c.take().unchecked_unwrap()(result))
}
// Non-generic version to reduce monomorphization cost
unsafe fn unpark_one_internal(key: usize,
callback: &mut FnMut(UnparkResult) -> UnparkToken)
-> UnparkResult {
unsafe fn unpark_one_internal(
key: usize,
callback: &mut FnMut(UnparkResult) -> UnparkToken,
) -> UnparkResult {
// Lock the bucket for the given key
let bucket = lock_bucket(key);
@ -818,28 +844,33 @@ pub unsafe fn unpark_all(key: usize, unpark_token: UnparkToken) -> usize {
/// The `validate` and `callback` functions are called while the queue is locked
/// and must not panic or call into any function in `parking_lot`.
#[inline]
pub unsafe fn unpark_requeue<V, C>(key_from: usize,
key_to: usize,
validate: V,
callback: C)
-> UnparkResult
where V: FnOnce() -> RequeueOp,
C: FnOnce(RequeueOp, UnparkResult) -> UnparkToken
pub unsafe fn unpark_requeue<V, C>(
key_from: usize,
key_to: usize,
validate: V,
callback: C,
) -> UnparkResult
where
V: FnOnce() -> RequeueOp,
C: FnOnce(RequeueOp, UnparkResult) -> UnparkToken,
{
let mut v = Some(validate);
let mut c = Some(callback);
unpark_requeue_internal(key_from,
key_to,
&mut || v.take().unchecked_unwrap()(),
&mut |op, r| c.take().unchecked_unwrap()(op, r))
unpark_requeue_internal(
key_from,
key_to,
&mut || v.take().unchecked_unwrap()(),
&mut |op, r| c.take().unchecked_unwrap()(op, r),
)
}
// Non-generic version to reduce monomorphization cost
unsafe fn unpark_requeue_internal(key_from: usize,
key_to: usize,
validate: &mut FnMut() -> RequeueOp,
callback: &mut FnMut(RequeueOp, UnparkResult) -> UnparkToken)
-> UnparkResult {
unsafe fn unpark_requeue_internal(
key_from: usize,
key_to: usize,
validate: &mut FnMut() -> RequeueOp,
callback: &mut FnMut(RequeueOp, UnparkResult) -> UnparkToken,
) -> UnparkResult {
// Lock the two buckets for the given key
let (bucket_from, bucket_to) = lock_bucket_pair(key_from, key_to);
@ -897,7 +928,9 @@ unsafe fn unpark_requeue_internal(key_from: usize,
if !requeue_threads.is_null() {
(*requeue_threads_tail).next_in_queue.set(ptr::null());
if !bucket_to.queue_head.get().is_null() {
(*bucket_to.queue_tail.get()).next_in_queue.set(requeue_threads);
(*bucket_to.queue_tail.get())
.next_in_queue
.set(requeue_threads);
} else {
bucket_to.queue_head.set(requeue_threads);
}
@ -951,18 +984,20 @@ unsafe fn unpark_requeue_internal(key_from: usize,
/// and must not panic or call into any function in `parking_lot`.
#[inline]
pub unsafe fn unpark_filter<F, C>(key: usize, mut filter: F, callback: C) -> UnparkResult
where F: FnMut(ParkToken) -> FilterOp,
C: FnOnce(UnparkResult) -> UnparkToken
where
F: FnMut(ParkToken) -> FilterOp,
C: FnOnce(UnparkResult) -> UnparkToken,
{
let mut c = Some(callback);
unpark_filter_internal(key, &mut filter, &mut |r| c.take().unchecked_unwrap()(r))
}
// Non-generic version to reduce monomorphization cost
unsafe fn unpark_filter_internal(key: usize,
filter: &mut FnMut(ParkToken) -> FilterOp,
callback: &mut FnMut(UnparkResult) -> UnparkToken)
-> UnparkResult {
unsafe fn unpark_filter_internal(
key: usize,
filter: &mut FnMut(ParkToken) -> FilterOp,
callback: &mut FnMut(UnparkResult) -> UnparkToken,
) -> UnparkResult {
// Lock the bucket for the given key
let bucket = lock_bucket(key);
@ -1035,3 +1070,323 @@ unsafe fn unpark_filter_internal(key: usize,
result
}
/// [Experimental] Deadlock detection
///
/// Enabled via the `deadlock_detection` feature flag.
pub mod deadlock {
#[cfg(feature = "deadlock_detection")]
use super::deadlock_impl;
#[cfg(feature = "deadlock_detection")]
pub(super) use super::deadlock_impl::DeadlockData;
#[cfg(not(feature = "deadlock_detection"))]
pub(super) struct DeadlockData {}
#[cfg(not(feature = "deadlock_detection"))]
impl DeadlockData {
pub(super) fn new() -> Self {
DeadlockData {}
}
}
/// Acquire a resource identified by key in the deadlock detector
/// Noop if deadlock_detection feature isn't enabled.
/// Note: Call after the resource is acquired
#[inline]
pub unsafe fn acquire_resource(_key: usize) {
#[cfg(feature = "deadlock_detection")]
deadlock_impl::acquire_resource(_key);
}
/// Release a resource identified by key in the deadlock detector.
/// Noop if deadlock_detection feature isn't enabled.
/// Note: Call before the resource is released
/// # Panics
/// Panics if the resource was already released or wasn't acquired in this thread.
#[inline]
pub unsafe fn release_resource(_key: usize) {
#[cfg(feature = "deadlock_detection")]
deadlock_impl::release_resource(_key);
}
/// Returns all deadlocks detected *since* the last call.
/// Each cycle consist of a vector of `DeadlockedThread`.
#[cfg(feature = "deadlock_detection")]
#[inline]
pub fn check_deadlock() -> Vec<Vec<deadlock_impl::DeadlockedThread>> {
deadlock_impl::check_deadlock()
}
#[inline]
pub(super) unsafe fn on_unpark(_td: &super::ThreadData) {
#[cfg(feature = "deadlock_detection")]
deadlock_impl::on_unpark(_td);
}
}
#[cfg(feature = "deadlock_detection")]
mod deadlock_impl {
use super::{get_hashtable, get_thread_data, lock_bucket, ThreadData, NUM_THREADS};
use std::cell::{Cell, UnsafeCell};
use std::sync::mpsc;
use std::sync::atomic::Ordering;
use std::collections::HashSet;
use thread_id;
use backtrace::Backtrace;
use petgraph;
use petgraph::graphmap::DiGraphMap;
/// Representation of a deadlocked thread
pub struct DeadlockedThread {
thread_id: usize,
backtrace: Backtrace,
}
impl DeadlockedThread {
/// The system thread id
pub fn thread_id(&self) -> usize {
self.thread_id
}
/// The thread backtrace
pub fn backtrace(&self) -> &Backtrace {
&self.backtrace
}
}
pub struct DeadlockData {
// Currently owned resources (keys)
resources: UnsafeCell<Vec<usize>>,
// Set when there's a pending callstack request
deadlocked: Cell<bool>,
// Sender used to report the backtrace
backtrace_sender: UnsafeCell<Option<mpsc::Sender<DeadlockedThread>>>,
// System thread id
thread_id: usize,
}
impl DeadlockData {
pub fn new() -> Self {
DeadlockData {
resources: UnsafeCell::new(Vec::new()),
deadlocked: Cell::new(false),
backtrace_sender: UnsafeCell::new(None),
thread_id: thread_id::get(),
}
}
}
pub(super) unsafe fn on_unpark(td: &ThreadData) {
if td.deadlock_data.deadlocked.get() {
let sender = (*td.deadlock_data.backtrace_sender.get()).take().unwrap();
sender
.send(DeadlockedThread {
thread_id: td.deadlock_data.thread_id,
backtrace: Backtrace::new(),
})
.unwrap();
// make sure to close this sender
drop(sender);
// park until the end of the time
td.parker.prepare_park();
td.parker.park();
unreachable!("unparked deadlocked thread!");
}
}
pub unsafe fn acquire_resource(key: usize) {
let mut thread_data = None;
let thread_data = get_thread_data(&mut thread_data);
(*thread_data.deadlock_data.resources.get()).push(key);
}
pub unsafe fn release_resource(key: usize) {
let mut thread_data = None;
let thread_data = get_thread_data(&mut thread_data);
let resources = &mut (*thread_data.deadlock_data.resources.get());
match resources.iter().rposition(|x| *x == key) {
Some(p) => resources.swap_remove(p),
None => panic!("key {} not found in thread resources", key),
};
}
pub fn check_deadlock() -> Vec<Vec<DeadlockedThread>> {
unsafe {
// fast pass
if check_wait_graph_fast() {
// double check
check_wait_graph_slow()
} else {
Vec::new()
}
}
}
// Simple algorithm that builds a wait graph f the threads and the resources,
// then checks for the presence of cycles (deadlocks).
// This variant isn't precise as it doesn't lock the entire table before checking
unsafe fn check_wait_graph_fast() -> bool {
let table = get_hashtable();
let thread_count = NUM_THREADS.load(Ordering::Relaxed);
let mut graph = DiGraphMap::<usize, ()>::with_capacity(thread_count * 2, thread_count * 2);
for b in &(*table).entries[..] {
b.mutex.lock();
let mut current = b.queue_head.get();
while !current.is_null() {
if !(*current).parked_with_timeout.get()
&& !(*current).deadlock_data.deadlocked.get()
{
// .resources are waiting for their owner
for &resource in &(*(*current).deadlock_data.resources.get()) {
graph.add_edge(resource, current as usize, ());
}
// owner waits for resource .key
graph.add_edge(current as usize, (*current).key.load(Ordering::Relaxed), ());
}
current = (*current).next_in_queue.get();
}
b.mutex.unlock();
}
petgraph::algo::is_cyclic_directed(&graph)
}
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
enum WaitGraphNode {
Thread(*const ThreadData),
Resource(usize),
}
use self::WaitGraphNode::*;
// Contrary to the _fast variant this locks the entrie table before looking for cycles.
// Returns all detected thread wait cycles.
// Note that once a cycle is reported it's never reported again.
unsafe fn check_wait_graph_slow() -> Vec<Vec<DeadlockedThread>> {
let mut table = get_hashtable();
loop {
// Lock all buckets in the old table
for b in &(*table).entries[..] {
b.mutex.lock();
}
// Now check if our table is still the latest one. Another thread could
// have grown the hash table between us getting and locking the hash table.
let new_table = get_hashtable();
if new_table == table {
break;
}
// Unlock buckets and try again
for b in &(*table).entries[..] {
b.mutex.unlock();
}
table = new_table;
}
let thread_count = NUM_THREADS.load(Ordering::Relaxed);
let mut graph =
DiGraphMap::<WaitGraphNode, ()>::with_capacity(thread_count * 2, thread_count * 2);
for b in &(*table).entries[..] {
let mut current = b.queue_head.get();
while !current.is_null() {
if !(*current).parked_with_timeout.get()
&& !(*current).deadlock_data.deadlocked.get()
{
// .resources are waiting for their owner
for &resource in &(*(*current).deadlock_data.resources.get()) {
graph.add_edge(Resource(resource), Thread(current), ());
}
// owner waits for resource .key
graph.add_edge(
Thread(current),
Resource((*current).key.load(Ordering::Relaxed)),
(),
);
}
current = (*current).next_in_queue.get();
}
}
for b in &(*table).entries[..] {
b.mutex.unlock();
}
// find cycles
let cycles = graph_cycles(&graph);
let mut results = Vec::with_capacity(cycles.len());
for cycle in cycles {
let (sender, receiver) = mpsc::channel();
for td in cycle {
let bucket = lock_bucket((*td).key.load(Ordering::Relaxed));
(*td).deadlock_data.deadlocked.set(true);
*(*td).deadlock_data.backtrace_sender.get() = Some(sender.clone());
let handle = (*td).parker.unpark_lock();
bucket.mutex.unlock();
// unpark the deadlocked thread!
// on unpark it'll notice the deadlocked flag and report back
handle.unpark();
}
// make sure to drop our sender before collecting results
drop(sender);
results.push(receiver.iter().collect());
}
results
}
// normalize a cycle to start with the "smallest" node
fn normalize_cycle<T: Ord + Copy + Clone>(input: &[T]) -> Vec<T> {
let min_pos = input
.iter()
.enumerate()
.min_by_key(|&(_, &t)| t)
.map(|(p, _)| p)
.unwrap_or(0);
input
.iter()
.cycle()
.skip(min_pos)
.take(input.len())
.cloned()
.collect()
}
// returns all thread cycles in the wait graph
fn graph_cycles(g: &DiGraphMap<WaitGraphNode, ()>) -> Vec<Vec<*const ThreadData>> {
use petgraph::visit::NodeIndexable;
use petgraph::visit::depth_first_search;
use petgraph::visit::DfsEvent;
let mut cycles = HashSet::new();
let mut path = Vec::with_capacity(g.node_bound());
// start from threads to get the correct threads cycle
let threads = g.nodes()
.filter(|n| if let &Thread(_) = n { true } else { false });
depth_first_search(g, threads, |e| match e {
DfsEvent::Discover(Thread(n), _) => path.push(n),
DfsEvent::Finish(Thread(_), _) => {
path.pop();
}
DfsEvent::BackEdge(_, Thread(n)) => {
let from = path.iter().rposition(|&i| i == n).unwrap();
cycles.insert(normalize_cycle(&path[from..]));
}
_ => (),
});
cycles.iter().cloned().collect()
}
}

View File

@ -12,7 +12,7 @@ use libc;
#[cfg(not(any(windows, unix)))]
use std::thread;
#[cfg(not(feature = "nightly"))]
use std::sync::atomic::{Ordering, fence};
use std::sync::atomic::{fence, Ordering};
// Yields the rest of the current timeslice to the OS
#[cfg(windows)]
@ -58,9 +58,8 @@ fn cpu_relax(iterations: u32) {
}
}
}
#[cfg(all(feature = "nightly", not(any(target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64"))))]
#[cfg(all(feature = "nightly",
not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))))]
#[inline]
fn cpu_relax(iterations: u32) {
for _ in 0..iterations {

View File

@ -10,7 +10,7 @@
use std::sync::atomic;
// Re-export this for convenience
pub use std::sync::atomic::{Ordering, fence};
pub use std::sync::atomic::{fence, Ordering};
// Wrapper around AtomicUsize for non-nightly which has usable compare_exchange
// and compare_exchange_weak methods.
@ -55,23 +55,33 @@ impl AtomicUsize {
self.0.fetch_or(val, order)
}
#[inline]
pub fn compare_exchange(&self,
old: usize,
new: usize,
order: Ordering,
_: Ordering)
-> Result<usize, usize> {
pub fn compare_exchange(
&self,
old: usize,
new: usize,
order: Ordering,
_: Ordering,
) -> Result<usize, usize> {
let res = self.0.compare_and_swap(old, new, order);
if res == old { Ok(res) } else { Err(res) }
if res == old {
Ok(res)
} else {
Err(res)
}
}
#[inline]
pub fn compare_exchange_weak(&self,
old: usize,
new: usize,
order: Ordering,
_: Ordering)
-> Result<usize, usize> {
pub fn compare_exchange_weak(
&self,
old: usize,
new: usize,
order: Ordering,
_: Ordering,
) -> Result<usize, usize> {
let res = self.0.compare_and_swap(old, new, order);
if res == old { Ok(res) } else { Err(res) }
if res == old {
Ok(res)
} else {
Err(res)
}
}
}

View File

@ -5,7 +5,7 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::sync::{Mutex, MutexGuard, Condvar};
use std::sync::{Condvar, Mutex, MutexGuard};
use std::cell::Cell;
use std::time::Instant;

View File

@ -29,7 +29,9 @@ pub struct ThreadParker {
impl ThreadParker {
pub fn new() -> ThreadParker {
ThreadParker { futex: AtomicI32::new(0) }
ThreadParker {
futex: AtomicI32::new(0),
}
}
// Prepares the parker. This should be called before adding it to the queue.
@ -50,8 +52,10 @@ impl ThreadParker {
let r = libc::syscall(SYS_FUTEX, &self.futex, FUTEX_WAIT | FUTEX_PRIVATE, 1, 0);
debug_assert!(r == 0 || r == -1);
if r == -1 {
debug_assert!(*libc::__errno_location() == libc::EINTR ||
*libc::__errno_location() == libc::EAGAIN);
debug_assert!(
*libc::__errno_location() == libc::EINTR
|| *libc::__errno_location() == libc::EAGAIN
);
}
}
}
@ -78,9 +82,11 @@ impl ThreadParker {
let r = libc::syscall(SYS_FUTEX, &self.futex, FUTEX_WAIT | FUTEX_PRIVATE, 1, &ts);
debug_assert!(r == 0 || r == -1);
if r == -1 {
debug_assert!(*libc::__errno_location() == libc::EINTR ||
*libc::__errno_location() == libc::EAGAIN ||
*libc::__errno_location() == libc::ETIMEDOUT);
debug_assert!(
*libc::__errno_location() == libc::EINTR
|| *libc::__errno_location() == libc::EAGAIN
|| *libc::__errno_location() == libc::ETIMEDOUT
);
}
}
true

View File

@ -124,7 +124,9 @@ impl ThreadParker {
let r = libc::pthread_mutex_lock(self.mutex.get());
debug_assert_eq!(r, 0);
UnparkHandle { thread_parker: self }
UnparkHandle {
thread_parker: self,
}
}
}

View File

@ -22,23 +22,26 @@ const STATE_TIMED_OUT: usize = 2;
#[allow(non_snake_case)]
pub struct KeyedEvent {
handle: winapi::HANDLE,
NtReleaseKeyedEvent: extern "system" fn(EventHandle: winapi::HANDLE,
Key: winapi::PVOID,
Alertable: winapi::BOOLEAN,
Timeout: winapi::PLARGE_INTEGER)
-> winapi::NTSTATUS,
NtWaitForKeyedEvent: extern "system" fn(EventHandle: winapi::HANDLE,
Key: winapi::PVOID,
Alertable: winapi::BOOLEAN,
Timeout: winapi::PLARGE_INTEGER)
-> winapi::NTSTATUS,
NtReleaseKeyedEvent: extern "system" fn(
EventHandle: winapi::HANDLE,
Key: winapi::PVOID,
Alertable: winapi::BOOLEAN,
Timeout: winapi::PLARGE_INTEGER,
) -> winapi::NTSTATUS,
NtWaitForKeyedEvent: extern "system" fn(
EventHandle: winapi::HANDLE,
Key: winapi::PVOID,
Alertable: winapi::BOOLEAN,
Timeout: winapi::PLARGE_INTEGER,
) -> winapi::NTSTATUS,
}
impl KeyedEvent {
unsafe fn wait_for(&self,
key: winapi::PVOID,
timeout: winapi::PLARGE_INTEGER)
-> winapi::NTSTATUS {
unsafe fn wait_for(
&self,
key: winapi::PVOID,
timeout: winapi::PLARGE_INTEGER,
) -> winapi::NTSTATUS {
(self.NtWaitForKeyedEvent)(self.handle, key, 0, timeout)
}
@ -69,17 +72,19 @@ impl KeyedEvent {
return None;
}
let NtCreateKeyedEvent: extern "system" fn(KeyedEventHandle: winapi::PHANDLE,
DesiredAccess: winapi::ACCESS_MASK,
ObjectAttributes: winapi::PVOID,
Flags: winapi::ULONG)
-> winapi::NTSTATUS =
mem::transmute(NtCreateKeyedEvent);
let NtCreateKeyedEvent: extern "system" fn(
KeyedEventHandle: winapi::PHANDLE,
DesiredAccess: winapi::ACCESS_MASK,
ObjectAttributes: winapi::PVOID,
Flags: winapi::ULONG,
) -> winapi::NTSTATUS = mem::transmute(NtCreateKeyedEvent);
let mut handle = mem::uninitialized();
let status = NtCreateKeyedEvent(&mut handle,
winapi::GENERIC_READ | winapi::GENERIC_WRITE,
ptr::null_mut(),
0);
let status = NtCreateKeyedEvent(
&mut handle,
winapi::GENERIC_READ | winapi::GENERIC_WRITE,
ptr::null_mut(),
0,
);
if status != winapi::STATUS_SUCCESS {
return None;
}
@ -122,7 +127,9 @@ impl KeyedEvent {
let diff = timeout - now;
let nt_timeout = (diff.as_secs() as winapi::LARGE_INTEGER)
.checked_mul(-10000000)
.and_then(|x| x.checked_sub((diff.subsec_nanos() as winapi::LARGE_INTEGER + 99) / 100));
.and_then(|x| {
x.checked_sub((diff.subsec_nanos() as winapi::LARGE_INTEGER + 99) / 100)
});
let mut nt_timeout = match nt_timeout {
Some(x) => x,
None => {

View File

@ -6,9 +6,9 @@
// copied, modified, or distributed except according to those terms.
#[cfg(feature = "nightly")]
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(not(feature = "nightly"))]
use stable::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use stable::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::time::Instant;
mod keyed_event;
@ -36,8 +36,10 @@ impl Backend {
} else if let Some(keyed_event) = keyed_event::KeyedEvent::create() {
backend = Backend::KeyedEvent(keyed_event);
} else {
panic!("parking_lot requires either NT Keyed Events (WinXP+) or \
WaitOnAddress/WakeByAddress (Win8+)");
panic!(
"parking_lot requires either NT Keyed Events (WinXP+) or \
WaitOnAddress/WakeByAddress (Win8+)"
);
}
// Try to create a new object

View File

@ -16,11 +16,12 @@ use kernel32;
#[allow(non_snake_case)]
pub struct WaitAddress {
WaitOnAddress: extern "system" fn(Address: winapi::PVOID,
CompareAddress: winapi::PVOID,
AddressSize: winapi::SIZE_T,
dwMilliseconds: winapi::DWORD)
-> winapi::BOOL,
WaitOnAddress: extern "system" fn(
Address: winapi::PVOID,
CompareAddress: winapi::PVOID,
AddressSize: winapi::SIZE_T,
dwMilliseconds: winapi::DWORD,
) -> winapi::BOOL,
WakeByAddressSingle: extern "system" fn(Address: winapi::PVOID),
}
@ -29,20 +30,21 @@ impl WaitAddress {
pub unsafe fn create() -> Option<WaitAddress> {
// MSDN claims that that WaitOnAddress and WakeByAddressSingle are
// located in kernel32.dll, but they are lying...
let synch_dll = kernel32::GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0"
.as_ptr() as winapi::LPCSTR);
let synch_dll = kernel32::GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr()
as winapi::LPCSTR);
if synch_dll.is_null() {
return None;
}
let WaitOnAddress = kernel32::GetProcAddress(synch_dll,
b"WaitOnAddress\0".as_ptr() as winapi::LPCSTR);
let WaitOnAddress =
kernel32::GetProcAddress(synch_dll, b"WaitOnAddress\0".as_ptr() as winapi::LPCSTR);
if WaitOnAddress.is_null() {
return None;
}
let WakeByAddressSingle = kernel32::GetProcAddress(synch_dll,
b"WakeByAddressSingle\0".as_ptr() as
winapi::LPCSTR);
let WakeByAddressSingle = kernel32::GetProcAddress(
synch_dll,
b"WakeByAddressSingle\0".as_ptr() as winapi::LPCSTR,
);
if WakeByAddressSingle.is_null() {
return None;
}
@ -63,10 +65,12 @@ impl WaitAddress {
pub unsafe fn park(&'static self, key: &AtomicUsize) {
while key.load(Ordering::Acquire) != 0 {
let cmp = 1usize;
let r = (self.WaitOnAddress)(key as *const _ as winapi::PVOID,
&cmp as *const _ as winapi::PVOID,
mem::size_of::<usize>() as winapi::SIZE_T,
winapi::INFINITE);
let r = (self.WaitOnAddress)(
key as *const _ as winapi::PVOID,
&cmp as *const _ as winapi::PVOID,
mem::size_of::<usize>() as winapi::SIZE_T,
winapi::INFINITE,
);
debug_assert!(r == winapi::TRUE);
}
}
@ -80,18 +84,24 @@ impl WaitAddress {
let diff = timeout - now;
let timeout = diff.as_secs()
.checked_mul(1000)
.and_then(|x| x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000))
.map(|ms| if ms > <winapi::DWORD>::max_value() as u64 {
winapi::INFINITE
} else {
ms as winapi::DWORD
.and_then(|x| {
x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000)
})
.map(|ms| {
if ms > <winapi::DWORD>::max_value() as u64 {
winapi::INFINITE
} else {
ms as winapi::DWORD
}
})
.unwrap_or(winapi::INFINITE);
let cmp = 1usize;
let r = (self.WaitOnAddress)(key as *const _ as winapi::PVOID,
&cmp as *const _ as winapi::PVOID,
mem::size_of::<usize>() as winapi::SIZE_T,
timeout);
let r = (self.WaitOnAddress)(
key as *const _ as winapi::PVOID,
&cmp as *const _ as winapi::PVOID,
mem::size_of::<usize>() as winapi::SIZE_T,
timeout,
);
if r == winapi::FALSE {
debug_assert_eq!(kernel32::GetLastError(), winapi::ERROR_TIMEOUT);
}

View File

@ -6,9 +6,9 @@
// copied, modified, or distributed except according to those terms.
#[cfg(feature = "nightly")]
use std::sync::atomic::{AtomicUsize, Ordering, fence};
use std::sync::atomic::{fence, AtomicUsize, Ordering};
#[cfg(not(feature = "nightly"))]
use stable::{AtomicUsize, Ordering, fence};
use stable::{fence, AtomicUsize, Ordering};
use std::ptr;
use std::mem;
use std::cell::Cell;
@ -89,14 +89,17 @@ pub struct WordLock {
impl WordLock {
#[inline]
pub fn new() -> WordLock {
WordLock { state: AtomicUsize::new(0) }
WordLock {
state: AtomicUsize::new(0),
}
}
#[inline]
pub unsafe fn lock(&self) {
if self.state
.compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
.is_ok() {
.is_ok()
{
return;
}
self.lock_slow();
@ -119,11 +122,12 @@ impl WordLock {
loop {
// Grab the lock if it isn't locked, even if there is a queue on it
if state & LOCKED_BIT == 0 {
match self.state
.compare_exchange_weak(state,
state | LOCKED_BIT,
Ordering::Acquire,
Ordering::Relaxed) {
match self.state.compare_exchange_weak(
state,
state | LOCKED_BIT,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => return,
Err(x) => state = x,
}
@ -152,11 +156,12 @@ impl WordLock {
thread_data.prev.set(ptr::null());
thread_data.next.set(queue_head);
}
if let Err(x) = self.state
.compare_exchange_weak(state,
(state & !QUEUE_MASK) | thread_data as *const _ as usize,
Ordering::Release,
Ordering::Relaxed) {
if let Err(x) = self.state.compare_exchange_weak(
state,
(state & !QUEUE_MASK) | thread_data as *const _ as usize,
Ordering::Release,
Ordering::Relaxed,
) {
state = x;
continue;
}
@ -183,10 +188,12 @@ impl WordLock {
}
// Try to grab the queue lock
match self.state.compare_exchange_weak(state,
state | QUEUE_LOCKED_BIT,
Ordering::Acquire,
Ordering::Relaxed) {
match self.state.compare_exchange_weak(
state,
state | QUEUE_LOCKED_BIT,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(x) => state = x,
}
@ -218,10 +225,12 @@ impl WordLock {
// thread now. Instead we let the next unlocker take care of waking
// up a thread.
if state & LOCKED_BIT != 0 {
match self.state.compare_exchange_weak(state,
state & !QUEUE_LOCKED_BIT,
Ordering::Release,
Ordering::Relaxed) {
match self.state.compare_exchange_weak(
state,
state & !QUEUE_LOCKED_BIT,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => return,
Err(x) => state = x,
}
@ -235,10 +244,12 @@ impl WordLock {
let new_tail = (*queue_tail).prev.get();
if new_tail.is_null() {
loop {
match self.state.compare_exchange_weak(state,
state & LOCKED_BIT,
Ordering::Release,
Ordering::Relaxed) {
match self.state.compare_exchange_weak(
state,
state & LOCKED_BIT,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(x) => state = x,
}

View File

@ -1 +0,0 @@
{"files":{".travis.yml":"91edce5ea2a1956399db4b17f580c8b7995af3aa9801c4314865f560c55d6d09","Cargo.toml":"107fc4138f10e17b1b3e7b3ac7f95787100fed0a170303f6bbfaee1c0eedea43","README.md":"ecca7edfce86fe7b219535e3c14721d1de838de7035de077a4d497959260bccc","benches/bench.rs":"54cf4879d36ba2a9f3423af91bb93227b70849200e5bf74e384a166d6aa09893","lib.rs":"2d6b7216296c2f1b1e44bf41c0567f5e47de5fa8a3d984200ef315e8b7939aa3"},"package":"8fcd03faf178110ab0334d74ca9631d77f94c8c11cc77fcb59538abf0025695d"}

View File

@ -1,14 +0,0 @@
language: rust
rust:
- nightly
- beta
- stable
script: |
cargo build --verbose &&
cargo build --all-features --verbose &&
cargo test --verbose &&
cargo test --all-features --verbose &&
([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --no-default-features) &&
([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench)
notifications:
webhooks: http://build.servo.org:54856/travis

View File

@ -1,40 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g. crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "smallvec"
version = "0.4.3"
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
description = "'Small vector' optimization: store up to a small number of items on the stack"
documentation = "http://doc.servo.org/smallvec/"
readme = "README.md"
keywords = ["small", "vec", "vector", "stack", "no_std"]
license = "MPL-2.0"
repository = "https://github.com/servo/rust-smallvec"
[lib]
name = "smallvec"
path = "lib.rs"
[dependencies.serde]
version = "1"
optional = true
[dependencies.heapsize]
version = "0.4"
optional = true
[dev-dependencies.bincode]
version = "0.8"
[features]
default = ["std"]
std = []
heapsizeof = ["heapsize", "std"]

View File

@ -1,6 +0,0 @@
rust-smallvec
=============
[Documentation](http://docs.rs/smallvec/)
"Small vector" optimization for Rust: store up to a small number of items on the stack

View File

@ -1,111 +0,0 @@
#![feature(test)]
extern crate smallvec;
extern crate test;
use smallvec::SmallVec;
use self::test::Bencher;
#[bench]
fn bench_push(b: &mut Bencher) {
#[inline(never)]
fn push_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
vec.push(x)
}
b.iter(|| {
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
for x in 0..100 {
push_noinline(&mut vec, x);
}
vec
});
}
#[bench]
fn bench_insert(b: &mut Bencher) {
#[inline(never)]
fn insert_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
vec.insert(0, x)
}
b.iter(|| {
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
for x in 0..100 {
insert_noinline(&mut vec, x);
}
vec
});
}
#[bench]
fn bench_insert_many(b: &mut Bencher) {
#[inline(never)]
fn insert_many_noinline<I: IntoIterator<Item=u64>>(
vec: &mut SmallVec<[u64; 16]>, index: usize, iterable: I) {
vec.insert_many(index, iterable)
}
b.iter(|| {
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
insert_many_noinline(&mut vec, 0, 0..100);
insert_many_noinline(&mut vec, 0, 0..100);
vec
});
}
#[bench]
fn bench_extend(b: &mut Bencher) {
b.iter(|| {
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
vec.extend(0..100);
vec
});
}
#[bench]
fn bench_from_slice(b: &mut Bencher) {
let v: Vec<u64> = (0..100).collect();
b.iter(|| {
let vec: SmallVec<[u64; 16]> = SmallVec::from_slice(&v);
vec
});
}
#[bench]
fn bench_extend_from_slice(b: &mut Bencher) {
let v: Vec<u64> = (0..100).collect();
b.iter(|| {
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
vec.extend_from_slice(&v);
vec
});
}
#[bench]
fn bench_insert_from_slice(b: &mut Bencher) {
let v: Vec<u64> = (0..100).collect();
b.iter(|| {
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
vec.insert_from_slice(0, &v);
vec.insert_from_slice(0, &v);
vec
});
}
#[bench]
fn bench_pushpop(b: &mut Bencher) {
#[inline(never)]
fn pushpop_noinline(vec: &mut SmallVec<[u64; 16]>, x: u64) {
vec.push(x);
vec.pop();
}
b.iter(|| {
let mut vec: SmallVec<[u64; 16]> = SmallVec::new();
for x in 0..100 {
pushpop_noinline(&mut vec, x);
}
vec
});
}

File diff suppressed because it is too large Load Diff

View File

@ -947,19 +947,19 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.2.4"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1225,11 +1225,6 @@ name = "smallbitvec"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.5.0"
@ -1684,7 +1679,7 @@ dependencies = [
"checksum ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da12c96037889ae0be29dd2bdd260e5a62a7df24e6466d5a15bb8131c1c200a8"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "37f364e2ce5efa24c7d0b6646d5bb61145551a0112f107ffd7499f1a3e322fbd"
"checksum parking_lot_core 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a25dd36576d01cca115881dc920b1f0dd0037303ed8cfa0c5d0a4966151757f7"
"checksum parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
@ -1715,7 +1710,6 @@ dependencies = [
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "79b776f00dfe01df905fa3b2eaa1659522e99e3fc4a7b1334171622205c4bdcf"
"checksum smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fcd03faf178110ab0334d74ca9631d77f94c8c11cc77fcb59538abf0025695d"
"checksum smallvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "872c0ff227000041c520cca51e883b858d388ab0ecf646bab76f065cebaec025"
"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"

View File

@ -935,19 +935,19 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.2.4"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1217,11 +1217,6 @@ name = "smallbitvec"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.5.0"
@ -1696,7 +1691,7 @@ dependencies = [
"checksum ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da12c96037889ae0be29dd2bdd260e5a62a7df24e6466d5a15bb8131c1c200a8"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "37f364e2ce5efa24c7d0b6646d5bb61145551a0112f107ffd7499f1a3e322fbd"
"checksum parking_lot_core 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a25dd36576d01cca115881dc920b1f0dd0037303ed8cfa0c5d0a4966151757f7"
"checksum parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
@ -1727,7 +1722,6 @@ dependencies = [
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "79b776f00dfe01df905fa3b2eaa1659522e99e3fc4a7b1334171622205c4bdcf"
"checksum smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fcd03faf178110ab0334d74ca9631d77f94c8c11cc77fcb59538abf0025695d"
"checksum smallvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "872c0ff227000041c520cca51e883b858d388ab0ecf646bab76f065cebaec025"
"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9"
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"