Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/parking_lot/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/parking_lot/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"8f41d6b5010e551d749f5d62bf0dbb27b3e3f06929b74677a246043357f7fdb9","Cargo.toml":"ce9c147a06b0827e0d04a11ff04c5706afa7c24b9394d160814015892e7e19c7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","README.md":"46c526d06e5b734d52e3b2b3242f20f8c8a229f730829f7f5282b8f669e1dea8","bors.toml":"938b40da0516dd1e18f6ab893d43a3c6a011836124696157568380f3ce784958","src/condvar.rs":"ce2952a4c17633f5b8e140c93d863931d3cd091725aa42e6141924c191233309","src/deadlock.rs":"7d3ebb5b4f63658435df277bb983e352e4bc651a92c4fd48ae68bf103e452d0d","src/elision.rs":"eefda6da9b3d1b41fa1f080e16cfb48606fc91599163c57fc9784a0722b2bdb5","src/fair_mutex.rs":"ca87b83837fa790c2cec40c728939dd2d5992a162da95c625227917d1b29840c","src/lib.rs":"1e63deb585c6802262bb255d4d7a50a210fb2a2cb5a3a6e2cad7cf8649463b40","src/mutex.rs":"0f9d6f8857a23dcb6f99cebe3d70b34881fa02b710db112e8424044de0797587","src/once.rs":"a2f49b2344f1d50f424147c97da791ce640aab48543494bd7fe772600ef94a16","src/raw_fair_mutex.rs":"316f954d9673ac5b8d6bf4c19f2444800f63daf801c224d986e2d6dac810643c","src/raw_mutex.rs":"4a79255673f47e167ce28d331c3b51f245017c7023e36c1c9b09baf00cc121c6","src/raw_rwlock.rs":"04700b4e9747a093872da7c7962a7049fc2600a1da7248098e160d05035a4c9d","src/remutex.rs":"7a0de55161cd57497bb52d3aecca69a89eff2e71cdb2d762df53579e0607b489","src/rwlock.rs":"13b1764dbcc62a6f4efbde60cba6a9ee45d6a21d6910cc1cf904ddf479033fa7","src/util.rs":"37a2c8b5c9254df83e8f3a5cd831558c1045061a76c2571bdc4d78eb86e467f2","tests/issue_203.rs":"5fbdf6ec63f391d86457df949678c203a1e81e8aa32d4e10037fa76e768702c0"},"package":"3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"}
|
||||
203
third-party/vendor/parking_lot/CHANGELOG.md
vendored
Normal file
203
third-party/vendor/parking_lot/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
## parking_lot 0.12.1 (2022-05-31)
|
||||
|
||||
- Fixed incorrect memory ordering in `RwLock`. (#344)
|
||||
- Added `Condvar::wait_while` convenience methods (#343)
|
||||
|
||||
## parking_lot_core 0.9.3 (2022-04-30)
|
||||
|
||||
- Bump windows-sys dependency to 0.36. (#339)
|
||||
|
||||
## parking_lot_core 0.9.2, lock_api 0.4.7 (2022-03-25)
|
||||
|
||||
- Enable const new() on lock types on stable. (#325)
|
||||
- Added `MutexGuard::leak` function. (#333)
|
||||
- Bump windows-sys dependency to 0.34. (#331)
|
||||
- Bump petgraph dependency to 0.6. (#326)
|
||||
- Don't use pthread attributes on the espidf platform. (#319)
|
||||
|
||||
## parking_lot_core 0.9.1 (2022-02-06)
|
||||
|
||||
- Bump windows-sys dependency to 0.32. (#316)
|
||||
|
||||
## parking_lot 0.12.0, parking_lot_core 0.9.0, lock_api 0.4.6 (2022-01-28)
|
||||
|
||||
- The MSRV is bumped to 1.49.0.
|
||||
- Disabled eventual fairness on wasm32-unknown-unknown. (#302)
|
||||
- Added a rwlock method to report if lock is held exclusively. (#303)
|
||||
- Use new `asm!` macro. (#304)
|
||||
- Use windows-rs instead of winapi for faster builds. (#311)
|
||||
- Moved hardware lock elision support to a separate Cargo feature. (#313)
|
||||
- Removed used of deprecated `spin_loop_hint`. (#314)
|
||||
|
||||
## parking_lot 0.11.2, parking_lot_core 0.8.4, lock_api 0.4.5 (2021-08-28)
|
||||
|
||||
- Fixed incorrect memory orderings on `RwLock` and `WordLock`. (#294, #292)
|
||||
- Added `Arc`-based lock guards. (#291)
|
||||
- Added workaround for TSan's lack of support for `fence`. (#292)
|
||||
|
||||
## lock_api 0.4.4 (2021-05-01)
|
||||
|
||||
- Update for latest nightly. (#281)
|
||||
|
||||
## lock_api 0.4.3 (2021-04-03)
|
||||
|
||||
- Added `[Raw]ReentrantMutex::is_owned`. (#280)
|
||||
|
||||
## parking_lot_core 0.8.3 (2021-02-12)
|
||||
|
||||
- Updated smallvec to 1.6. (#276)
|
||||
|
||||
## parking_lot_core 0.8.2 (2020-12-21)
|
||||
|
||||
- Fixed assertion failure on OpenBSD. (#270)
|
||||
|
||||
## parking_lot_core 0.8.1 (2020-12-04)
|
||||
|
||||
- Removed deprecated CloudABI support. (#263)
|
||||
- Fixed build on wasm32-unknown-unknown. (#265)
|
||||
- Relaxed dependency on `smallvec`. (#266)
|
||||
|
||||
## parking_lot 0.11.1, lock_api 0.4.2 (2020-11-18)
|
||||
|
||||
- Fix bounds on Send and Sync impls for lock guards. (#262)
|
||||
- Fix incorrect memory ordering in `RwLock`. (#260)
|
||||
|
||||
## lock_api 0.4.1 (2020-07-06)
|
||||
|
||||
- Add `data_ptr` method to lock types to allow unsafely accessing the inner data
|
||||
without a guard. (#247)
|
||||
|
||||
## parking_lot 0.11.0, parking_lot_core 0.8.0, lock_api 0.4.0 (2020-06-23)
|
||||
|
||||
- Add `is_locked` method to mutex types. (#235)
|
||||
- Make `RawReentrantMutex` public. (#233)
|
||||
- Allow lock guard to be sent to another thread with the `send_guard` feature. (#240)
|
||||
- Use `Instant` type from the `instant` crate on wasm32-unknown-unknown. (#231)
|
||||
- Remove deprecated and unsound `MappedRwLockWriteGuard::downgrade`. (#244)
|
||||
- Most methods on the `Raw*` traits have been made unsafe since they assume
|
||||
the current thread holds the lock. (#243)
|
||||
|
||||
## parking_lot_core 0.7.2 (2020-04-21)
|
||||
|
||||
- Add support for `wasm32-unknown-unknown` under the "nightly" feature. (#226)
|
||||
|
||||
## parking_lot 0.10.2 (2020-04-10)
|
||||
|
||||
- Update minimum version of `lock_api`.
|
||||
|
||||
## parking_lot 0.10.1, parking_lot_core 0.7.1, lock_api 0.3.4 (2020-04-10)
|
||||
|
||||
- Add methods to construct `Mutex`, `RwLock`, etc in a `const` context. (#217)
|
||||
- Add `FairMutex` which always uses fair unlocking. (#204)
|
||||
- Fixed panic with deadlock detection on macOS. (#203)
|
||||
- Fixed incorrect synchronization in `create_hashtable`. (#210)
|
||||
- Use `llvm_asm!` instead of the deprecated `asm!`. (#223)
|
||||
|
||||
## lock_api 0.3.3 (2020-01-04)
|
||||
|
||||
- Deprecate unsound `MappedRwLockWriteGuard::downgrade` (#198)
|
||||
|
||||
## parking_lot 0.10.0, parking_lot_core 0.7.0, lock_api 0.3.2 (2019-11-25)
|
||||
|
||||
- Upgrade smallvec dependency to 1.0 in parking_lot_core.
|
||||
- Replace all usage of `mem::uninitialized` with `mem::MaybeUninit`.
|
||||
- The minimum required Rust version is bumped to 1.36. Because of the above two changes.
|
||||
- Make methods on `WaitTimeoutResult` and `OnceState` take `self` by value instead of reference.
|
||||
|
||||
## parking_lot_core 0.6.2 (2019-07-22)
|
||||
|
||||
- Fixed compile error on Windows with old cfg_if version. (#164)
|
||||
|
||||
## parking_lot_core 0.6.1 (2019-07-17)
|
||||
|
||||
- Fixed Android build. (#163)
|
||||
|
||||
## parking_lot 0.9.0, parking_lot_core 0.6.0, lock_api 0.3.1 (2019-07-14)
|
||||
|
||||
- Re-export lock_api (0.3.1) from parking_lot (#150)
|
||||
- Removed (non-dev) dependency on rand crate for fairness mechanism, by
|
||||
including a simple xorshift PRNG in core (#144)
|
||||
- Android now uses the futex-based ThreadParker. (#140)
|
||||
- Fixed CloudABI ThreadParker. (#140)
|
||||
- Fix race condition in lock_api::ReentrantMutex (da16c2c7)
|
||||
|
||||
## lock_api 0.3.0 (2019-07-03, _yanked_)
|
||||
|
||||
- Use NonZeroUsize in GetThreadId::nonzero_thread_id (#148)
|
||||
- Debug assert lock_count in ReentrantMutex (#148)
|
||||
- Tag as `unsafe` and document some internal methods (#148)
|
||||
- This release was _yanked_ due to a regression in ReentrantMutex (da16c2c7)
|
||||
|
||||
## parking_lot 0.8.1 (2019-07-03, _yanked_)
|
||||
|
||||
- Re-export lock_api (0.3.0) from parking_lot (#150)
|
||||
- This release was _yanked_ from crates.io due to unexpected breakage (#156)
|
||||
|
||||
## parking_lot 0.8.0, parking_lot_core 0.5.0, lock_api 0.2.0 (2019-05-04)
|
||||
|
||||
- Fix race conditions in deadlock detection.
|
||||
- Support for more platforms by adding ThreadParker implementations for
|
||||
Wasm, Redox, SGX and CloudABI.
|
||||
- Drop support for older Rust. parking_lot now requires 1.31 and is a
|
||||
Rust 2018 edition crate (#122).
|
||||
- Disable the owning_ref feature by default.
|
||||
- Fix was_last_thread value in the timeout callback of park() (#129).
|
||||
- Support single byte Mutex/Once on stable Rust when compiler is at least
|
||||
version 1.34.
|
||||
- Make Condvar::new and Once::new const fns on stable Rust and remove
|
||||
ONCE_INIT (#134).
|
||||
- Add optional Serde support (#135).
|
||||
|
||||
## parking_lot 0.7.1 (2019-01-01)
|
||||
|
||||
- Fixed potential deadlock when upgrading a RwLock.
|
||||
- Fixed overflow panic on very long timeouts (#111).
|
||||
|
||||
## parking_lot 0.7.0, parking_lot_core 0.4.0 (2018-11-26)
|
||||
|
||||
- Return if or how many threads were notified from `Condvar::notify_*`
|
||||
|
||||
## parking_lot 0.6.3 (2018-07-18)
|
||||
|
||||
- Export `RawMutex`, `RawRwLock` and `RawThreadId`.
|
||||
|
||||
## parking_lot 0.6.2 (2018-06-18)
|
||||
|
||||
- Enable `lock_api/nightly` feature from `parking_lot/nightly` (#79)
|
||||
|
||||
## parking_lot 0.6.1 (2018-06-08)
|
||||
|
||||
Added missing typedefs for mapped lock guards:
|
||||
|
||||
- `MappedMutexGuard`
|
||||
- `MappedReentrantMutexGuard`
|
||||
- `MappedRwLockReadGuard`
|
||||
- `MappedRwLockWriteGuard`
|
||||
|
||||
## parking_lot 0.6.0 (2018-06-08)
|
||||
|
||||
This release moves most of the code for type-safe `Mutex` and `RwLock` types
|
||||
into a separate crate called `lock_api`. This new crate is compatible with
|
||||
`no_std` and provides `Mutex` and `RwLock` type-safe wrapper types from a raw
|
||||
mutex type which implements the `RawMutex` or `RawRwLock` trait. The API
|
||||
provided by the wrapper types can be extended by implementing more traits on
|
||||
the raw mutex type which provide more functionality (e.g. `RawMutexTimed`). See
|
||||
the crate documentation for more details.
|
||||
|
||||
There are also several major changes:
|
||||
|
||||
- The minimum required Rust version is bumped to 1.26.
|
||||
- All methods on `MutexGuard` (and other guard types) are no longer inherent
|
||||
methods and must be called as `MutexGuard::method(self)`. This avoids
|
||||
conflicts with methods from the inner type.
|
||||
- `MutexGuard` (and other guard types) add the `unlocked` method which
|
||||
temporarily unlocks a mutex, runs the given closure, and then re-locks the
|
||||
mutex.
|
||||
- `MutexGuard` (and other guard types) add the `bump` method which gives a
|
||||
chance for other threads to acquire the mutex by temporarily unlocking it and
|
||||
re-locking it. However this is optimized for the common case where there are
|
||||
no threads waiting on the lock, in which case no unlocking is performed.
|
||||
- `MutexGuard` (and other guard types) add the `map` method which returns a
|
||||
`MappedMutexGuard` which holds only a subset of the original locked type. The
|
||||
`MappedMutexGuard` type is identical to `MutexGuard` except that it does not
|
||||
support the `unlocked` and `bump` methods, and can't be used with `CondVar`.
|
||||
53
third-party/vendor/parking_lot/Cargo.toml
vendored
Normal file
53
third-party/vendor/parking_lot/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# 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 are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
authors = ["Amanieu d'Antras <amanieu@gmail.com>"]
|
||||
description = "More compact and efficient implementations of the standard synchronization primitives."
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"mutex",
|
||||
"condvar",
|
||||
"rwlock",
|
||||
"once",
|
||||
"thread",
|
||||
]
|
||||
categories = ["concurrency"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/Amanieu/parking_lot"
|
||||
|
||||
[dependencies.lock_api]
|
||||
version = "0.4.6"
|
||||
|
||||
[dependencies.parking_lot_core]
|
||||
version = "0.9.0"
|
||||
|
||||
[dev-dependencies.bincode]
|
||||
version = "1.3.3"
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.8.3"
|
||||
|
||||
[features]
|
||||
arc_lock = ["lock_api/arc_lock"]
|
||||
deadlock_detection = ["parking_lot_core/deadlock_detection"]
|
||||
default = []
|
||||
hardware-lock-elision = []
|
||||
nightly = [
|
||||
"parking_lot_core/nightly",
|
||||
"lock_api/nightly",
|
||||
]
|
||||
owning_ref = ["lock_api/owning_ref"]
|
||||
send_guard = []
|
||||
serde = ["lock_api/serde"]
|
||||
201
third-party/vendor/parking_lot/LICENSE-APACHE
vendored
Normal file
201
third-party/vendor/parking_lot/LICENSE-APACHE
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
third-party/vendor/parking_lot/LICENSE-MIT
vendored
Normal file
25
third-party/vendor/parking_lot/LICENSE-MIT
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2016 The Rust Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
156
third-party/vendor/parking_lot/README.md
vendored
Normal file
156
third-party/vendor/parking_lot/README.md
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
parking_lot
|
||||
============
|
||||
|
||||
[](https://github.com/Amanieu/parking_lot/actions)
|
||||
[](https://crates.io/crates/parking_lot)
|
||||
|
||||
[Documentation (synchronization primitives)](https://docs.rs/parking_lot/)
|
||||
|
||||
[Documentation (core parking lot API)](https://docs.rs/parking_lot_core/)
|
||||
|
||||
[Documentation (type-safe lock API)](https://docs.rs/lock_api/)
|
||||
|
||||
This library provides implementations of `Mutex`, `RwLock`, `Condvar` and
|
||||
`Once` that are smaller, faster and more flexible than those in the Rust
|
||||
standard library, as well as a `ReentrantMutex` type which supports recursive
|
||||
locking. It also exposes a low-level API for creating your own efficient
|
||||
synchronization primitives.
|
||||
|
||||
When tested on x86_64 Linux, `parking_lot::Mutex` was found to be 1.5x
|
||||
faster than `std::sync::Mutex` when uncontended, and up to 5x faster when
|
||||
contended from multiple threads. The numbers for `RwLock` vary depending on
|
||||
the number of reader and writer threads, but are almost always faster than
|
||||
the standard library `RwLock`, and even up to 50x faster in some cases.
|
||||
|
||||
## Features
|
||||
|
||||
The primitives provided by this library have several advantages over those
|
||||
in the Rust standard library:
|
||||
|
||||
1. `Mutex` and `Once` only require 1 byte of storage space, while `Condvar`
|
||||
and `RwLock` only require 1 word of storage space. On the other hand the
|
||||
standard library primitives require a dynamically allocated `Box` to hold
|
||||
OS-specific synchronization primitives. The small size of `Mutex` in
|
||||
particular encourages the use of fine-grained locks to increase
|
||||
parallelism.
|
||||
2. Since they consist of just a single atomic variable, have constant
|
||||
initializers and don't need destructors, these primitives can be used as
|
||||
`static` global variables. The standard library primitives require
|
||||
dynamic initialization and thus need to be lazily initialized with
|
||||
`lazy_static!`.
|
||||
3. Uncontended lock acquisition and release is done through fast inline
|
||||
paths which only require a single atomic operation.
|
||||
4. Microcontention (a contended lock with a short critical section) is
|
||||
efficiently handled by spinning a few times while trying to acquire a
|
||||
lock.
|
||||
5. The locks are adaptive and will suspend a thread after a few failed spin
|
||||
attempts. This makes the locks suitable for both long and short critical
|
||||
sections.
|
||||
6. `Condvar`, `RwLock` and `Once` work on Windows XP, unlike the standard
|
||||
library versions of those types.
|
||||
7. `RwLock` takes advantage of hardware lock elision on processors that
|
||||
support it, which can lead to huge performance wins with many readers.
|
||||
This must be enabled with the `hardware-lock-elision` feature.
|
||||
8. `RwLock` uses a task-fair locking policy, which avoids reader and writer
|
||||
starvation, whereas the standard library version makes no guarantees.
|
||||
9. `Condvar` is guaranteed not to produce spurious wakeups. A thread will
|
||||
only be woken up if it timed out or it was woken up by a notification.
|
||||
10. `Condvar::notify_all` will only wake up a single thread and requeue the
|
||||
rest to wait on the associated `Mutex`. This avoids a thundering herd
|
||||
problem where all threads try to acquire the lock at the same time.
|
||||
11. `RwLock` supports atomically downgrading a write lock into a read lock.
|
||||
12. `Mutex` and `RwLock` allow raw unlocking without a RAII guard object.
|
||||
13. `Mutex<()>` and `RwLock<()>` allow raw locking without a RAII guard
|
||||
object.
|
||||
14. `Mutex` and `RwLock` support [eventual fairness](https://trac.webkit.org/changeset/203350)
|
||||
which allows them to be fair on average without sacrificing performance.
|
||||
15. A `ReentrantMutex` type which supports recursive locking.
|
||||
16. An *experimental* deadlock detector that works for `Mutex`,
|
||||
`RwLock` and `ReentrantMutex`. This feature is disabled by default and
|
||||
can be enabled via the `deadlock_detection` feature.
|
||||
17. `RwLock` supports atomically upgrading an "upgradable" read lock into a
|
||||
write lock.
|
||||
18. Optional support for [serde](https://docs.serde.rs/serde/). Enable via the
|
||||
feature `serde`. **NOTE!** this support is for `Mutex`, `ReentrantMutex`,
|
||||
and `RwLock` only; `Condvar` and `Once` are not currently supported.
|
||||
19. Lock guards can be sent to other threads when the `send_guard` feature is
|
||||
enabled.
|
||||
|
||||
## The parking lot
|
||||
|
||||
To keep these primitives small, all thread queuing and suspending
|
||||
functionality is offloaded to the *parking lot*. The idea behind this is
|
||||
based on the Webkit [`WTF::ParkingLot`](https://webkit.org/blog/6161/locking-in-webkit/)
|
||||
class, which essentially consists of a hash table mapping of lock addresses
|
||||
to queues of parked (sleeping) threads. The Webkit parking lot was itself
|
||||
inspired by Linux [futexes](https://man7.org/linux/man-pages/man2/futex.2.html),
|
||||
but it is more powerful since it allows invoking callbacks while holding a queue
|
||||
lock.
|
||||
|
||||
## Nightly vs stable
|
||||
|
||||
There are a few restrictions when using this library on stable Rust:
|
||||
|
||||
- The `wasm32-unknown-unknown` target is only fully supported on nightly with
|
||||
`-C target-feature=+atomics` in `RUSTFLAGS` and `-Z build-std` passed to cargo.
|
||||
parking_lot will work mostly fine on stable, the only difference is it will
|
||||
panic instead of block forever if you hit a deadlock.
|
||||
Just make sure not to enable `-C target-feature=+atomics` on stable as that
|
||||
will allow wasm to run with multiple threads which will completely break
|
||||
parking_lot's concurrency guarantees.
|
||||
|
||||
To enable nightly-only functionality, you need to enable the `nightly` feature
|
||||
in Cargo (see below).
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
parking_lot = "0.12"
|
||||
```
|
||||
|
||||
To enable nightly-only features, add this to your `Cargo.toml` instead:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
parking_lot = { version = "0.12", features = ["nightly"] }
|
||||
```
|
||||
|
||||
The experimental deadlock detector can be enabled with the
|
||||
`deadlock_detection` Cargo feature.
|
||||
|
||||
To allow sending `MutexGuard`s and `RwLock*Guard`s to other threads, enable the
|
||||
`send_guard` option.
|
||||
|
||||
Note that the `deadlock_detection` and `send_guard` features are incompatible
|
||||
and cannot be used together.
|
||||
|
||||
Hardware lock elision support for x86 can be enabled with the
|
||||
`hardware-lock-elision` feature. This requires Rust 1.59 due to the use of
|
||||
inline assembly.
|
||||
|
||||
The core parking lot API is provided by the `parking_lot_core` crate. It is
|
||||
separate from the synchronization primitives in the `parking_lot` crate so that
|
||||
changes to the core API do not cause breaking changes for users of `parking_lot`.
|
||||
|
||||
## Minimum Rust version
|
||||
|
||||
The current minimum required Rust version is 1.49. Any change to this is
|
||||
considered a breaking change and will require a major version bump.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
|
||||
additional terms or conditions.
|
||||
4
third-party/vendor/parking_lot/bors.toml
vendored
Normal file
4
third-party/vendor/parking_lot/bors.toml
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
status = [
|
||||
"build_tier_one",
|
||||
"build_other_platforms",
|
||||
]
|
||||
1271
third-party/vendor/parking_lot/src/condvar.rs
vendored
Normal file
1271
third-party/vendor/parking_lot/src/condvar.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
232
third-party/vendor/parking_lot/src/deadlock.rs
vendored
Normal file
232
third-party/vendor/parking_lot/src/deadlock.rs
vendored
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
//! \[Experimental\] Deadlock detection
|
||||
//!
|
||||
//! This feature is optional and can be enabled via the `deadlock_detection` feature flag.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! #[cfg(feature = "deadlock_detection")]
|
||||
//! { // only for #[cfg]
|
||||
//! use std::thread;
|
||||
//! use std::time::Duration;
|
||||
//! use parking_lot::deadlock;
|
||||
//!
|
||||
//! // Create a background thread which checks for deadlocks every 10s
|
||||
//! thread::spawn(move || {
|
||||
//! loop {
|
||||
//! thread::sleep(Duration::from_secs(10));
|
||||
//! let deadlocks = deadlock::check_deadlock();
|
||||
//! if deadlocks.is_empty() {
|
||||
//! continue;
|
||||
//! }
|
||||
//!
|
||||
//! println!("{} deadlocks detected", deadlocks.len());
|
||||
//! for (i, threads) in deadlocks.iter().enumerate() {
|
||||
//! println!("Deadlock #{}", i);
|
||||
//! for t in threads {
|
||||
//! println!("Thread Id {:#?}", t.thread_id());
|
||||
//! println!("{:#?}", t.backtrace());
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! });
|
||||
//! } // only for #[cfg]
|
||||
//! ```
|
||||
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
pub use parking_lot_core::deadlock::check_deadlock;
|
||||
pub(crate) use parking_lot_core::deadlock::{acquire_resource, release_resource};
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
mod tests {
|
||||
use crate::{Mutex, ReentrantMutex, RwLock};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::thread::{self, sleep};
|
||||
use std::time::Duration;
|
||||
|
||||
// We need to serialize these tests since deadlock detection uses global state
|
||||
static DEADLOCK_DETECTION_LOCK: Mutex<()> = crate::const_mutex(());
|
||||
|
||||
fn check_deadlock() -> bool {
|
||||
use parking_lot_core::deadlock::check_deadlock;
|
||||
!check_deadlock().is_empty()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_deadlock() {
|
||||
let _guard = DEADLOCK_DETECTION_LOCK.lock();
|
||||
|
||||
let m1: Arc<Mutex<()>> = Default::default();
|
||||
let m2: Arc<Mutex<()>> = Default::default();
|
||||
let m3: Arc<Mutex<()>> = Default::default();
|
||||
let b = Arc::new(Barrier::new(4));
|
||||
|
||||
let m1_ = m1.clone();
|
||||
let m2_ = m2.clone();
|
||||
let m3_ = m3.clone();
|
||||
let b1 = b.clone();
|
||||
let b2 = b.clone();
|
||||
let b3 = b.clone();
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
let _t1 = thread::spawn(move || {
|
||||
let _g = m1.lock();
|
||||
b1.wait();
|
||||
let _ = m2_.lock();
|
||||
});
|
||||
|
||||
let _t2 = thread::spawn(move || {
|
||||
let _g = m2.lock();
|
||||
b2.wait();
|
||||
let _ = m3_.lock();
|
||||
});
|
||||
|
||||
let _t3 = thread::spawn(move || {
|
||||
let _g = m3.lock();
|
||||
b3.wait();
|
||||
let _ = m1_.lock();
|
||||
});
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
b.wait();
|
||||
sleep(Duration::from_millis(50));
|
||||
assert!(check_deadlock());
|
||||
|
||||
assert!(!check_deadlock());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_deadlock_reentrant() {
|
||||
let _guard = DEADLOCK_DETECTION_LOCK.lock();
|
||||
|
||||
let m1: Arc<Mutex<()>> = Default::default();
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
let _t1 = thread::spawn(move || {
|
||||
let _g = m1.lock();
|
||||
let _ = m1.lock();
|
||||
});
|
||||
|
||||
sleep(Duration::from_millis(50));
|
||||
assert!(check_deadlock());
|
||||
|
||||
assert!(!check_deadlock());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remutex_deadlock() {
|
||||
let _guard = DEADLOCK_DETECTION_LOCK.lock();
|
||||
|
||||
let m1: Arc<ReentrantMutex<()>> = Default::default();
|
||||
let m2: Arc<ReentrantMutex<()>> = Default::default();
|
||||
let m3: Arc<ReentrantMutex<()>> = Default::default();
|
||||
let b = Arc::new(Barrier::new(4));
|
||||
|
||||
let m1_ = m1.clone();
|
||||
let m2_ = m2.clone();
|
||||
let m3_ = m3.clone();
|
||||
let b1 = b.clone();
|
||||
let b2 = b.clone();
|
||||
let b3 = b.clone();
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
let _t1 = thread::spawn(move || {
|
||||
let _g = m1.lock();
|
||||
let _g = m1.lock();
|
||||
b1.wait();
|
||||
let _ = m2_.lock();
|
||||
});
|
||||
|
||||
let _t2 = thread::spawn(move || {
|
||||
let _g = m2.lock();
|
||||
let _g = m2.lock();
|
||||
b2.wait();
|
||||
let _ = m3_.lock();
|
||||
});
|
||||
|
||||
let _t3 = thread::spawn(move || {
|
||||
let _g = m3.lock();
|
||||
let _g = m3.lock();
|
||||
b3.wait();
|
||||
let _ = m1_.lock();
|
||||
});
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
b.wait();
|
||||
sleep(Duration::from_millis(50));
|
||||
assert!(check_deadlock());
|
||||
|
||||
assert!(!check_deadlock());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_deadlock() {
|
||||
let _guard = DEADLOCK_DETECTION_LOCK.lock();
|
||||
|
||||
let m1: Arc<RwLock<()>> = Default::default();
|
||||
let m2: Arc<RwLock<()>> = Default::default();
|
||||
let m3: Arc<RwLock<()>> = Default::default();
|
||||
let b = Arc::new(Barrier::new(4));
|
||||
|
||||
let m1_ = m1.clone();
|
||||
let m2_ = m2.clone();
|
||||
let m3_ = m3.clone();
|
||||
let b1 = b.clone();
|
||||
let b2 = b.clone();
|
||||
let b3 = b.clone();
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
let _t1 = thread::spawn(move || {
|
||||
let _g = m1.read();
|
||||
b1.wait();
|
||||
let _g = m2_.write();
|
||||
});
|
||||
|
||||
let _t2 = thread::spawn(move || {
|
||||
let _g = m2.read();
|
||||
b2.wait();
|
||||
let _g = m3_.write();
|
||||
});
|
||||
|
||||
let _t3 = thread::spawn(move || {
|
||||
let _g = m3.read();
|
||||
b3.wait();
|
||||
let _ = m1_.write();
|
||||
});
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
b.wait();
|
||||
sleep(Duration::from_millis(50));
|
||||
assert!(check_deadlock());
|
||||
|
||||
assert!(!check_deadlock());
|
||||
}
|
||||
|
||||
#[cfg(rwlock_deadlock_detection_not_supported)]
|
||||
#[test]
|
||||
fn test_rwlock_deadlock_reentrant() {
|
||||
let _guard = DEADLOCK_DETECTION_LOCK.lock();
|
||||
|
||||
let m1: Arc<RwLock<()>> = Default::default();
|
||||
|
||||
assert!(!check_deadlock());
|
||||
|
||||
let _t1 = thread::spawn(move || {
|
||||
let _g = m1.read();
|
||||
let _ = m1.write();
|
||||
});
|
||||
|
||||
sleep(Duration::from_millis(50));
|
||||
assert!(check_deadlock());
|
||||
|
||||
assert!(!check_deadlock());
|
||||
}
|
||||
}
|
||||
112
third-party/vendor/parking_lot/src/elision.rs
vendored
Normal file
112
third-party/vendor/parking_lot/src/elision.rs
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(all(feature = "hardware-lock-elision", any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
use std::arch::asm;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
// Extension trait to add lock elision primitives to atomic types
|
||||
pub trait AtomicElisionExt {
|
||||
type IntType;
|
||||
|
||||
// Perform a compare_exchange and start a transaction
|
||||
fn elision_compare_exchange_acquire(
|
||||
&self,
|
||||
current: Self::IntType,
|
||||
new: Self::IntType,
|
||||
) -> Result<Self::IntType, Self::IntType>;
|
||||
|
||||
// Perform a fetch_sub and end a transaction
|
||||
fn elision_fetch_sub_release(&self, val: Self::IntType) -> Self::IntType;
|
||||
}
|
||||
|
||||
// Indicates whether the target architecture supports lock elision
|
||||
#[inline]
|
||||
pub fn have_elision() -> bool {
|
||||
cfg!(all(
|
||||
feature = "hardware-lock-elision",
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
))
|
||||
}
|
||||
|
||||
// This implementation is never actually called because it is guarded by
|
||||
// have_elision().
|
||||
#[cfg(not(all(feature = "hardware-lock-elision", any(target_arch = "x86", target_arch = "x86_64"))))]
|
||||
impl AtomicElisionExt for AtomicUsize {
|
||||
type IntType = usize;
|
||||
|
||||
#[inline]
|
||||
fn elision_compare_exchange_acquire(&self, _: usize, _: usize) -> Result<usize, usize> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn elision_fetch_sub_release(&self, _: usize) -> usize {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "hardware-lock-elision", any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
impl AtomicElisionExt for AtomicUsize {
|
||||
type IntType = usize;
|
||||
|
||||
#[inline]
|
||||
fn elision_compare_exchange_acquire(&self, current: usize, new: usize) -> Result<usize, usize> {
|
||||
unsafe {
|
||||
use core::arch::asm;
|
||||
let prev: usize;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
asm!(
|
||||
"xacquire",
|
||||
"lock",
|
||||
"cmpxchg [{:e}], {:e}",
|
||||
in(reg) self,
|
||||
in(reg) new,
|
||||
inout("eax") current => prev,
|
||||
);
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
asm!(
|
||||
"xacquire",
|
||||
"lock",
|
||||
"cmpxchg [{}], {}",
|
||||
in(reg) self,
|
||||
in(reg) new,
|
||||
inout("rax") current => prev,
|
||||
);
|
||||
if prev == current {
|
||||
Ok(prev)
|
||||
} else {
|
||||
Err(prev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn elision_fetch_sub_release(&self, val: usize) -> usize {
|
||||
unsafe {
|
||||
use core::arch::asm;
|
||||
let prev: usize;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
asm!(
|
||||
"xrelease",
|
||||
"lock",
|
||||
"xadd [{:e}], {:e}",
|
||||
in(reg) self,
|
||||
inout(reg) val.wrapping_neg() => prev,
|
||||
);
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
asm!(
|
||||
"xrelease",
|
||||
"lock",
|
||||
"xadd [{}], {}",
|
||||
in(reg) self,
|
||||
inout(reg) val.wrapping_neg() => prev,
|
||||
);
|
||||
prev
|
||||
}
|
||||
}
|
||||
}
|
||||
275
third-party/vendor/parking_lot/src/fair_mutex.rs
vendored
Normal file
275
third-party/vendor/parking_lot/src/fair_mutex.rs
vendored
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use crate::raw_fair_mutex::RawFairMutex;
|
||||
use lock_api;
|
||||
|
||||
/// A mutual exclusive primitive that is always fair, useful for protecting shared data
|
||||
///
|
||||
/// This mutex will block threads waiting for the lock to become available. The
|
||||
/// mutex can be statically initialized or created by the `new`
|
||||
/// constructor. Each mutex has a type parameter which represents the data that
|
||||
/// it is protecting. The data can only be accessed through the RAII guards
|
||||
/// returned from `lock` and `try_lock`, which guarantees that the data is only
|
||||
/// ever accessed when the mutex is locked.
|
||||
///
|
||||
/// The regular mutex provided by `parking_lot` uses eventual fairness
|
||||
/// (after some time it will default to the fair algorithm), but eventual
|
||||
/// fairness does not provide the same guarantees an always fair method would.
|
||||
/// Fair mutexes are generally slower, but sometimes needed.
|
||||
///
|
||||
/// In a fair mutex the waiters form a queue, and the lock is always granted to
|
||||
/// the next requester in the queue, in first-in first-out order. This ensures
|
||||
/// that one thread cannot starve others by quickly re-acquiring the lock after
|
||||
/// releasing it.
|
||||
///
|
||||
/// A fair mutex may not be interesting if threads have different priorities (this is known as
|
||||
/// priority inversion).
|
||||
///
|
||||
/// # Differences from the standard library `Mutex`
|
||||
///
|
||||
/// - No poisoning, the lock is released normally on panic.
|
||||
/// - Only requires 1 byte of space, whereas the standard library boxes the
|
||||
/// `FairMutex` due to platform limitations.
|
||||
/// - Can be statically constructed.
|
||||
/// - Does not require any drop glue when dropped.
|
||||
/// - Inline fast path for the uncontended case.
|
||||
/// - Efficient handling of micro-contention using adaptive spinning.
|
||||
/// - Allows raw locking & unlocking without a guard.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use parking_lot::FairMutex;
|
||||
/// use std::sync::{Arc, mpsc::channel};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// const N: usize = 10;
|
||||
///
|
||||
/// // Spawn a few threads to increment a shared variable (non-atomically), and
|
||||
/// // let the main thread know once all increments are done.
|
||||
/// //
|
||||
/// // Here we're using an Arc to share memory among threads, and the data inside
|
||||
/// // the Arc is protected with a mutex.
|
||||
/// let data = Arc::new(FairMutex::new(0));
|
||||
///
|
||||
/// let (tx, rx) = channel();
|
||||
/// for _ in 0..10 {
|
||||
/// let (data, tx) = (Arc::clone(&data), tx.clone());
|
||||
/// thread::spawn(move || {
|
||||
/// // The shared state can only be accessed once the lock is held.
|
||||
/// // Our non-atomic increment is safe because we're the only thread
|
||||
/// // which can access the shared state when the lock is held.
|
||||
/// let mut data = data.lock();
|
||||
/// *data += 1;
|
||||
/// if *data == N {
|
||||
/// tx.send(()).unwrap();
|
||||
/// }
|
||||
/// // the lock is unlocked here when `data` goes out of scope.
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// rx.recv().unwrap();
|
||||
/// ```
|
||||
pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;
|
||||
|
||||
/// Creates a new fair mutex in an unlocked state ready for use.
|
||||
///
|
||||
/// This allows creating a fair mutex in a constant context on stable Rust.
|
||||
pub const fn const_fair_mutex<T>(val: T) -> FairMutex<T> {
|
||||
FairMutex::const_new(<RawFairMutex as lock_api::RawMutex>::INIT, val)
|
||||
}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
|
||||
/// dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// `Deref` and `DerefMut` implementations.
|
||||
pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>;
|
||||
|
||||
/// An RAII mutex guard returned by `FairMutexGuard::map`, which can point to a
|
||||
/// subfield of the protected data.
|
||||
///
|
||||
/// The main difference between `MappedFairMutexGuard` and `FairMutexGuard` is that the
|
||||
/// former doesn't support temporarily unlocking and re-locking, since that
|
||||
/// could introduce soundness issues if the locked object is modified by another
|
||||
/// thread.
|
||||
pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::FairMutex;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopy(i32);
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let m = FairMutex::new(());
|
||||
drop(m.lock());
|
||||
drop(m.lock());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lots_and_lots() {
|
||||
const J: u32 = 1000;
|
||||
const K: u32 = 3;
|
||||
|
||||
let m = Arc::new(FairMutex::new(0));
|
||||
|
||||
fn inc(m: &FairMutex<u32>) {
|
||||
for _ in 0..J {
|
||||
*m.lock() += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..K {
|
||||
let tx2 = tx.clone();
|
||||
let m2 = m.clone();
|
||||
thread::spawn(move || {
|
||||
inc(&m2);
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
let tx2 = tx.clone();
|
||||
let m2 = m.clone();
|
||||
thread::spawn(move || {
|
||||
inc(&m2);
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
for _ in 0..2 * K {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
assert_eq!(*m.lock(), J * K * 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_lock() {
|
||||
let m = FairMutex::new(());
|
||||
*m.try_lock().unwrap() = ();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let m = FairMutex::new(NonCopy(10));
|
||||
assert_eq!(m.into_inner(), NonCopy(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner_drop() {
|
||||
struct Foo(Arc<AtomicUsize>);
|
||||
impl Drop for Foo {
|
||||
fn drop(&mut self) {
|
||||
self.0.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
let num_drops = Arc::new(AtomicUsize::new(0));
|
||||
let m = FairMutex::new(Foo(num_drops.clone()));
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
|
||||
{
|
||||
let _inner = m.into_inner();
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
|
||||
}
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut m = FairMutex::new(NonCopy(10));
|
||||
*m.get_mut() = NonCopy(20);
|
||||
assert_eq!(m.into_inner(), NonCopy(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_arc_nested() {
|
||||
// Tests nested mutexes and access
|
||||
// to underlying data.
|
||||
let arc = Arc::new(FairMutex::new(1));
|
||||
let arc2 = Arc::new(FairMutex::new(arc));
|
||||
let (tx, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
let lock = arc2.lock();
|
||||
let lock2 = lock.lock();
|
||||
assert_eq!(*lock2, 1);
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_arc_access_in_unwind() {
|
||||
let arc = Arc::new(FairMutex::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
struct Unwinder {
|
||||
i: Arc<FairMutex<i32>>,
|
||||
}
|
||||
impl Drop for Unwinder {
|
||||
fn drop(&mut self) {
|
||||
*self.i.lock() += 1;
|
||||
}
|
||||
}
|
||||
let _u = Unwinder { i: arc2 };
|
||||
panic!();
|
||||
})
|
||||
.join();
|
||||
let lock = arc.lock();
|
||||
assert_eq!(*lock, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_unsized() {
|
||||
let mutex: &FairMutex<[i32]> = &FairMutex::new([1, 2, 3]);
|
||||
{
|
||||
let b = &mut *mutex.lock();
|
||||
b[0] = 4;
|
||||
b[2] = 5;
|
||||
}
|
||||
let comp: &[i32] = &[4, 2, 5];
|
||||
assert_eq!(&*mutex.lock(), comp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutexguard_sync() {
|
||||
fn sync<T: Sync>(_: T) {}
|
||||
|
||||
let mutex = FairMutex::new(());
|
||||
sync(mutex.lock());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_debug() {
|
||||
let mutex = FairMutex::new(vec![0u8, 10]);
|
||||
|
||||
assert_eq!(format!("{:?}", mutex), "Mutex { data: [0, 10] }");
|
||||
let _lock = mutex.lock();
|
||||
assert_eq!(format!("{:?}", mutex), "Mutex { data: <locked> }");
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn test_serde() {
|
||||
let contents: Vec<u8> = vec![0, 1, 2];
|
||||
let mutex = FairMutex::new(contents.clone());
|
||||
|
||||
let serialized = serialize(&mutex).unwrap();
|
||||
let deserialized: FairMutex<Vec<u8>> = deserialize(&serialized).unwrap();
|
||||
|
||||
assert_eq!(*(mutex.lock()), *(deserialized.lock()));
|
||||
assert_eq!(contents, *(deserialized.lock()));
|
||||
}
|
||||
}
|
||||
56
third-party/vendor/parking_lot/src/lib.rs
vendored
Normal file
56
third-party/vendor/parking_lot/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! This library provides implementations of `Mutex`, `RwLock`, `Condvar` and
|
||||
//! `Once` that are smaller, faster and more flexible than those in the Rust
|
||||
//! standard library. It also provides a `ReentrantMutex` type.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
mod condvar;
|
||||
mod elision;
|
||||
mod fair_mutex;
|
||||
mod mutex;
|
||||
mod once;
|
||||
mod raw_fair_mutex;
|
||||
mod raw_mutex;
|
||||
mod raw_rwlock;
|
||||
mod remutex;
|
||||
mod rwlock;
|
||||
mod util;
|
||||
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
pub mod deadlock;
|
||||
#[cfg(not(feature = "deadlock_detection"))]
|
||||
mod deadlock;
|
||||
|
||||
// If deadlock detection is enabled, we cannot allow lock guards to be sent to
|
||||
// other threads.
|
||||
#[cfg(all(feature = "send_guard", feature = "deadlock_detection"))]
|
||||
compile_error!("the `send_guard` and `deadlock_detection` features cannot be used together");
|
||||
#[cfg(feature = "send_guard")]
|
||||
type GuardMarker = lock_api::GuardSend;
|
||||
#[cfg(not(feature = "send_guard"))]
|
||||
type GuardMarker = lock_api::GuardNoSend;
|
||||
|
||||
pub use self::condvar::{Condvar, WaitTimeoutResult};
|
||||
pub use self::fair_mutex::{const_fair_mutex, FairMutex, FairMutexGuard, MappedFairMutexGuard};
|
||||
pub use self::mutex::{const_mutex, MappedMutexGuard, Mutex, MutexGuard};
|
||||
pub use self::once::{Once, OnceState};
|
||||
pub use self::raw_fair_mutex::RawFairMutex;
|
||||
pub use self::raw_mutex::RawMutex;
|
||||
pub use self::raw_rwlock::RawRwLock;
|
||||
pub use self::remutex::{
|
||||
const_reentrant_mutex, MappedReentrantMutexGuard, RawThreadId, ReentrantMutex,
|
||||
ReentrantMutexGuard,
|
||||
};
|
||||
pub use self::rwlock::{
|
||||
const_rwlock, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard,
|
||||
RwLockUpgradableReadGuard, RwLockWriteGuard,
|
||||
};
|
||||
pub use ::lock_api;
|
||||
312
third-party/vendor/parking_lot/src/mutex.rs
vendored
Normal file
312
third-party/vendor/parking_lot/src/mutex.rs
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use crate::raw_mutex::RawMutex;
|
||||
use lock_api;
|
||||
|
||||
/// A mutual exclusion primitive useful for protecting shared data
|
||||
///
|
||||
/// This mutex will block threads waiting for the lock to become available. The
|
||||
/// mutex can be statically initialized or created by the `new`
|
||||
/// constructor. Each mutex has a type parameter which represents the data that
|
||||
/// it is protecting. The data can only be accessed through the RAII guards
|
||||
/// returned from `lock` and `try_lock`, which guarantees that the data is only
|
||||
/// ever accessed when the mutex is locked.
|
||||
///
|
||||
/// # Fairness
|
||||
///
|
||||
/// A typical unfair lock can often end up in a situation where a single thread
|
||||
/// quickly acquires and releases the same mutex in succession, which can starve
|
||||
/// other threads waiting to acquire the mutex. While this improves throughput
|
||||
/// because it doesn't force a context switch when a thread tries to re-acquire
|
||||
/// a mutex it has just released, this can starve other threads.
|
||||
///
|
||||
/// This mutex uses [eventual fairness](https://trac.webkit.org/changeset/203350)
|
||||
/// to ensure that the lock will be fair on average without sacrificing
|
||||
/// throughput. This is done by forcing a fair unlock on average every 0.5ms,
|
||||
/// which will force the lock to go to the next thread waiting for the mutex.
|
||||
///
|
||||
/// Additionally, any critical section longer than 1ms will always use a fair
|
||||
/// unlock, which has a negligible impact on throughput considering the length
|
||||
/// of the critical section.
|
||||
///
|
||||
/// You can also force a fair unlock by calling `MutexGuard::unlock_fair` when
|
||||
/// unlocking a mutex instead of simply dropping the `MutexGuard`.
|
||||
///
|
||||
/// # Differences from the standard library `Mutex`
|
||||
///
|
||||
/// - No poisoning, the lock is released normally on panic.
|
||||
/// - Only requires 1 byte of space, whereas the standard library boxes the
|
||||
/// `Mutex` due to platform limitations.
|
||||
/// - Can be statically constructed.
|
||||
/// - Does not require any drop glue when dropped.
|
||||
/// - Inline fast path for the uncontended case.
|
||||
/// - Efficient handling of micro-contention using adaptive spinning.
|
||||
/// - Allows raw locking & unlocking without a guard.
|
||||
/// - Supports eventual fairness so that the mutex is fair on average.
|
||||
/// - Optionally allows making the mutex fair by calling `MutexGuard::unlock_fair`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use parking_lot::Mutex;
|
||||
/// use std::sync::{Arc, mpsc::channel};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// const N: usize = 10;
|
||||
///
|
||||
/// // Spawn a few threads to increment a shared variable (non-atomically), and
|
||||
/// // let the main thread know once all increments are done.
|
||||
/// //
|
||||
/// // Here we're using an Arc to share memory among threads, and the data inside
|
||||
/// // the Arc is protected with a mutex.
|
||||
/// let data = Arc::new(Mutex::new(0));
|
||||
///
|
||||
/// let (tx, rx) = channel();
|
||||
/// for _ in 0..10 {
|
||||
/// let (data, tx) = (Arc::clone(&data), tx.clone());
|
||||
/// thread::spawn(move || {
|
||||
/// // The shared state can only be accessed once the lock is held.
|
||||
/// // Our non-atomic increment is safe because we're the only thread
|
||||
/// // which can access the shared state when the lock is held.
|
||||
/// let mut data = data.lock();
|
||||
/// *data += 1;
|
||||
/// if *data == N {
|
||||
/// tx.send(()).unwrap();
|
||||
/// }
|
||||
/// // the lock is unlocked here when `data` goes out of scope.
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// rx.recv().unwrap();
|
||||
/// ```
|
||||
pub type Mutex<T> = lock_api::Mutex<RawMutex, T>;
|
||||
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
///
|
||||
/// This allows creating a mutex in a constant context on stable Rust.
|
||||
pub const fn const_mutex<T>(val: T) -> Mutex<T> {
|
||||
Mutex::const_new(<RawMutex as lock_api::RawMutex>::INIT, val)
|
||||
}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
|
||||
/// dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// `Deref` and `DerefMut` implementations.
|
||||
pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>;
|
||||
|
||||
/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a
|
||||
/// subfield of the protected data.
|
||||
///
|
||||
/// The main difference between `MappedMutexGuard` and `MutexGuard` is that the
|
||||
/// former doesn't support temporarily unlocking and re-locking, since that
|
||||
/// could introduce soundness issues if the locked object is modified by another
|
||||
/// thread.
|
||||
pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Condvar, Mutex};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopy(i32);
|
||||
|
||||
unsafe impl<T: Send> Send for Packet<T> {}
|
||||
unsafe impl<T> Sync for Packet<T> {}
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let m = Mutex::new(());
|
||||
drop(m.lock());
|
||||
drop(m.lock());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lots_and_lots() {
|
||||
const J: u32 = 1000;
|
||||
const K: u32 = 3;
|
||||
|
||||
let m = Arc::new(Mutex::new(0));
|
||||
|
||||
fn inc(m: &Mutex<u32>) {
|
||||
for _ in 0..J {
|
||||
*m.lock() += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..K {
|
||||
let tx2 = tx.clone();
|
||||
let m2 = m.clone();
|
||||
thread::spawn(move || {
|
||||
inc(&m2);
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
let tx2 = tx.clone();
|
||||
let m2 = m.clone();
|
||||
thread::spawn(move || {
|
||||
inc(&m2);
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
for _ in 0..2 * K {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
assert_eq!(*m.lock(), J * K * 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_lock() {
|
||||
let m = Mutex::new(());
|
||||
*m.try_lock().unwrap() = ();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let m = Mutex::new(NonCopy(10));
|
||||
assert_eq!(m.into_inner(), NonCopy(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner_drop() {
|
||||
struct Foo(Arc<AtomicUsize>);
|
||||
impl Drop for Foo {
|
||||
fn drop(&mut self) {
|
||||
self.0.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
let num_drops = Arc::new(AtomicUsize::new(0));
|
||||
let m = Mutex::new(Foo(num_drops.clone()));
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
|
||||
{
|
||||
let _inner = m.into_inner();
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
|
||||
}
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut m = Mutex::new(NonCopy(10));
|
||||
*m.get_mut() = NonCopy(20);
|
||||
assert_eq!(m.into_inner(), NonCopy(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_arc_condvar() {
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
let (tx, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
// wait until parent gets in
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
let mut lock = lock.lock();
|
||||
*lock = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut lock = lock.lock();
|
||||
tx.send(()).unwrap();
|
||||
assert!(!*lock);
|
||||
while !*lock {
|
||||
cvar.wait(&mut lock);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_arc_nested() {
|
||||
// Tests nested mutexes and access
|
||||
// to underlying data.
|
||||
let arc = Arc::new(Mutex::new(1));
|
||||
let arc2 = Arc::new(Mutex::new(arc));
|
||||
let (tx, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
let lock = arc2.lock();
|
||||
let lock2 = lock.lock();
|
||||
assert_eq!(*lock2, 1);
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_arc_access_in_unwind() {
|
||||
let arc = Arc::new(Mutex::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
struct Unwinder {
|
||||
i: Arc<Mutex<i32>>,
|
||||
}
|
||||
impl Drop for Unwinder {
|
||||
fn drop(&mut self) {
|
||||
*self.i.lock() += 1;
|
||||
}
|
||||
}
|
||||
let _u = Unwinder { i: arc2 };
|
||||
panic!();
|
||||
})
|
||||
.join();
|
||||
let lock = arc.lock();
|
||||
assert_eq!(*lock, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_unsized() {
|
||||
let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]);
|
||||
{
|
||||
let b = &mut *mutex.lock();
|
||||
b[0] = 4;
|
||||
b[2] = 5;
|
||||
}
|
||||
let comp: &[i32] = &[4, 2, 5];
|
||||
assert_eq!(&*mutex.lock(), comp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutexguard_sync() {
|
||||
fn sync<T: Sync>(_: T) {}
|
||||
|
||||
let mutex = Mutex::new(());
|
||||
sync(mutex.lock());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_debug() {
|
||||
let mutex = Mutex::new(vec![0u8, 10]);
|
||||
|
||||
assert_eq!(format!("{:?}", mutex), "Mutex { data: [0, 10] }");
|
||||
let _lock = mutex.lock();
|
||||
assert_eq!(format!("{:?}", mutex), "Mutex { data: <locked> }");
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn test_serde() {
|
||||
let contents: Vec<u8> = vec![0, 1, 2];
|
||||
let mutex = Mutex::new(contents.clone());
|
||||
|
||||
let serialized = serialize(&mutex).unwrap();
|
||||
let deserialized: Mutex<Vec<u8>> = deserialize(&serialized).unwrap();
|
||||
|
||||
assert_eq!(*(mutex.lock()), *(deserialized.lock()));
|
||||
assert_eq!(contents, *(deserialized.lock()));
|
||||
}
|
||||
}
|
||||
458
third-party/vendor/parking_lot/src/once.rs
vendored
Normal file
458
third-party/vendor/parking_lot/src/once.rs
vendored
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use crate::util::UncheckedOptionExt;
|
||||
use core::{
|
||||
fmt, mem,
|
||||
sync::atomic::{fence, AtomicU8, Ordering},
|
||||
};
|
||||
use parking_lot_core::{self, SpinWait, DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN};
|
||||
|
||||
const DONE_BIT: u8 = 1;
|
||||
const POISON_BIT: u8 = 2;
|
||||
const LOCKED_BIT: u8 = 4;
|
||||
const PARKED_BIT: u8 = 8;
|
||||
|
||||
/// Current state of a `Once`.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum OnceState {
|
||||
/// A closure has not been executed yet
|
||||
New,
|
||||
|
||||
/// A closure was executed but panicked.
|
||||
Poisoned,
|
||||
|
||||
/// A thread is currently executing a closure.
|
||||
InProgress,
|
||||
|
||||
/// A closure has completed successfully.
|
||||
Done,
|
||||
}
|
||||
|
||||
impl OnceState {
|
||||
/// Returns whether the associated `Once` has been poisoned.
|
||||
///
|
||||
/// Once an initialization routine for a `Once` has panicked it will forever
|
||||
/// indicate to future forced initialization routines that it is poisoned.
|
||||
#[inline]
|
||||
pub fn poisoned(self) -> bool {
|
||||
match self {
|
||||
OnceState::Poisoned => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the associated `Once` has successfully executed a
|
||||
/// closure.
|
||||
#[inline]
|
||||
pub fn done(self) -> bool {
|
||||
match self {
|
||||
OnceState::Done => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A synchronization primitive which can be used to run a one-time
|
||||
/// initialization. Useful for one-time initialization for globals, FFI or
|
||||
/// related functionality.
|
||||
///
|
||||
/// # Differences from the standard library `Once`
|
||||
///
|
||||
/// - Only requires 1 byte of space, instead of 1 word.
|
||||
/// - Not required to be `'static`.
|
||||
/// - Relaxed memory barriers in the fast path, which can significantly improve
|
||||
/// performance on some architectures.
|
||||
/// - Efficient handling of micro-contention using adaptive spinning.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use parking_lot::Once;
|
||||
///
|
||||
/// static START: Once = Once::new();
|
||||
///
|
||||
/// START.call_once(|| {
|
||||
/// // run initialization here
|
||||
/// });
|
||||
/// ```
|
||||
pub struct Once(AtomicU8);
|
||||
|
||||
impl Once {
|
||||
/// Creates a new `Once` value.
|
||||
#[inline]
|
||||
pub const fn new() -> Once {
|
||||
Once(AtomicU8::new(0))
|
||||
}
|
||||
|
||||
/// Returns the current state of this `Once`.
|
||||
#[inline]
|
||||
pub fn state(&self) -> OnceState {
|
||||
let state = self.0.load(Ordering::Acquire);
|
||||
if state & DONE_BIT != 0 {
|
||||
OnceState::Done
|
||||
} else if state & LOCKED_BIT != 0 {
|
||||
OnceState::InProgress
|
||||
} else if state & POISON_BIT != 0 {
|
||||
OnceState::Poisoned
|
||||
} else {
|
||||
OnceState::New
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an initialization routine once and only once. The given closure
|
||||
/// will be executed if this is the first time `call_once` has been called,
|
||||
/// and otherwise the routine will *not* be invoked.
|
||||
///
|
||||
/// This method will block the calling thread if another initialization
|
||||
/// routine is currently running.
|
||||
///
|
||||
/// When this function returns, it is guaranteed that some initialization
|
||||
/// has run and completed (it may not be the closure specified). It is also
|
||||
/// guaranteed that any memory writes performed by the executed closure can
|
||||
/// be reliably observed by other threads at this point (there is a
|
||||
/// happens-before relation between the closure and code executing after the
|
||||
/// return).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use parking_lot::Once;
|
||||
///
|
||||
/// static mut VAL: usize = 0;
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// // Accessing a `static mut` is unsafe much of the time, but if we do so
|
||||
/// // in a synchronized fashion (e.g. write once or read all) then we're
|
||||
/// // good to go!
|
||||
/// //
|
||||
/// // This function will only call `expensive_computation` once, and will
|
||||
/// // otherwise always return the value returned from the first invocation.
|
||||
/// fn get_cached_val() -> usize {
|
||||
/// unsafe {
|
||||
/// INIT.call_once(|| {
|
||||
/// VAL = expensive_computation();
|
||||
/// });
|
||||
/// VAL
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn expensive_computation() -> usize {
|
||||
/// // ...
|
||||
/// # 2
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The closure `f` will only be executed once if this is called
|
||||
/// concurrently amongst many threads. If that closure panics, however, then
|
||||
/// it will *poison* this `Once` instance, causing all future invocations of
|
||||
/// `call_once` to also panic.
|
||||
#[inline]
|
||||
pub fn call_once<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
if self.0.load(Ordering::Acquire) == DONE_BIT {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
self.call_once_slow(false, &mut |_| unsafe { f.take().unchecked_unwrap()() });
|
||||
}
|
||||
|
||||
/// Performs the same function as `call_once` except ignores poisoning.
|
||||
///
|
||||
/// If this `Once` has been poisoned (some initialization panicked) then
|
||||
/// this function will continue to attempt to call initialization functions
|
||||
/// until one of them doesn't panic.
|
||||
///
|
||||
/// The closure `f` is yielded a structure which can be used to query the
|
||||
/// state of this `Once` (whether initialization has previously panicked or
|
||||
/// not).
|
||||
#[inline]
|
||||
pub fn call_once_force<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(OnceState),
|
||||
{
|
||||
if self.0.load(Ordering::Acquire) == DONE_BIT {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
self.call_once_slow(true, &mut |state| unsafe {
|
||||
f.take().unchecked_unwrap()(state)
|
||||
});
|
||||
}
|
||||
|
||||
// This is a non-generic function to reduce the monomorphization cost of
|
||||
// using `call_once` (this isn't exactly a trivial or small implementation).
|
||||
//
|
||||
// Additionally, this is tagged with `#[cold]` as it should indeed be cold
|
||||
// and it helps let LLVM know that calls to this function should be off the
|
||||
// fast path. Essentially, this should help generate more straight line code
|
||||
// in LLVM.
|
||||
//
|
||||
// Finally, this takes an `FnMut` instead of a `FnOnce` because there's
|
||||
// currently no way to take an `FnOnce` and call it via virtual dispatch
|
||||
// without some allocation overhead.
|
||||
#[cold]
|
||||
fn call_once_slow(&self, ignore_poison: bool, f: &mut dyn FnMut(OnceState)) {
|
||||
let mut spinwait = SpinWait::new();
|
||||
let mut state = self.0.load(Ordering::Relaxed);
|
||||
loop {
|
||||
// If another thread called the closure, we're done
|
||||
if state & DONE_BIT != 0 {
|
||||
// An acquire fence is needed here since we didn't load the
|
||||
// state with Ordering::Acquire.
|
||||
fence(Ordering::Acquire);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the state has been poisoned and we aren't forcing, then panic
|
||||
if state & POISON_BIT != 0 && !ignore_poison {
|
||||
// Need the fence here as well for the same reason
|
||||
fence(Ordering::Acquire);
|
||||
panic!("Once instance has previously been poisoned");
|
||||
}
|
||||
|
||||
// Grab the lock if it isn't locked, even if there is a queue on it.
|
||||
// We also clear the poison bit since we are going to try running
|
||||
// the closure again.
|
||||
if state & LOCKED_BIT == 0 {
|
||||
match self.0.compare_exchange_weak(
|
||||
state,
|
||||
(state | LOCKED_BIT) & !POISON_BIT,
|
||||
Ordering::Acquire,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => break,
|
||||
Err(x) => state = x,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there is no queue, try spinning a few times
|
||||
if state & PARKED_BIT == 0 && spinwait.spin() {
|
||||
state = self.0.load(Ordering::Relaxed);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the parked bit
|
||||
if state & PARKED_BIT == 0 {
|
||||
if let Err(x) = self.0.compare_exchange_weak(
|
||||
state,
|
||||
state | PARKED_BIT,
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
state = x;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Park our thread until we are woken up by the thread that owns the
|
||||
// lock.
|
||||
let addr = self as *const _ as usize;
|
||||
let validate = || self.0.load(Ordering::Relaxed) == LOCKED_BIT | PARKED_BIT;
|
||||
let before_sleep = || {};
|
||||
let timed_out = |_, _| unreachable!();
|
||||
unsafe {
|
||||
parking_lot_core::park(
|
||||
addr,
|
||||
validate,
|
||||
before_sleep,
|
||||
timed_out,
|
||||
DEFAULT_PARK_TOKEN,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
// Loop back and check if the done bit was set
|
||||
spinwait.reset();
|
||||
state = self.0.load(Ordering::Relaxed);
|
||||
}
|
||||
|
||||
struct PanicGuard<'a>(&'a Once);
|
||||
impl<'a> Drop for PanicGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
// Mark the state as poisoned, unlock it and unpark all threads.
|
||||
let once = self.0;
|
||||
let state = once.0.swap(POISON_BIT, Ordering::Release);
|
||||
if state & PARKED_BIT != 0 {
|
||||
let addr = once as *const _ as usize;
|
||||
unsafe {
|
||||
parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we have the lock, so run the closure. Make sure we
|
||||
// properly clean up if the closure panicks.
|
||||
let guard = PanicGuard(self);
|
||||
let once_state = if state & POISON_BIT != 0 {
|
||||
OnceState::Poisoned
|
||||
} else {
|
||||
OnceState::New
|
||||
};
|
||||
f(once_state);
|
||||
mem::forget(guard);
|
||||
|
||||
// Now unlock the state, set the done bit and unpark all threads
|
||||
let state = self.0.swap(DONE_BIT, Ordering::Release);
|
||||
if state & PARKED_BIT != 0 {
|
||||
let addr = self as *const _ as usize;
|
||||
unsafe {
|
||||
parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Once {
|
||||
#[inline]
|
||||
fn default() -> Once {
|
||||
Once::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Once {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Once")
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Once;
|
||||
use std::panic;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::thread;
|
||||
|
||||
#[test]
|
||||
fn smoke_once() {
|
||||
static O: Once = Once::new();
|
||||
let mut a = 0;
|
||||
O.call_once(|| a += 1);
|
||||
assert_eq!(a, 1);
|
||||
O.call_once(|| a += 1);
|
||||
assert_eq!(a, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stampede_once() {
|
||||
static O: Once = Once::new();
|
||||
static mut RUN: bool = false;
|
||||
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..10 {
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
for _ in 0..4 {
|
||||
thread::yield_now()
|
||||
}
|
||||
unsafe {
|
||||
O.call_once(|| {
|
||||
assert!(!RUN);
|
||||
RUN = true;
|
||||
});
|
||||
assert!(RUN);
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
unsafe {
|
||||
O.call_once(|| {
|
||||
assert!(!RUN);
|
||||
RUN = true;
|
||||
});
|
||||
assert!(RUN);
|
||||
}
|
||||
|
||||
for _ in 0..10 {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poison_bad() {
|
||||
static O: Once = Once::new();
|
||||
|
||||
// poison the once
|
||||
let t = panic::catch_unwind(|| {
|
||||
O.call_once(|| panic!());
|
||||
});
|
||||
assert!(t.is_err());
|
||||
|
||||
// poisoning propagates
|
||||
let t = panic::catch_unwind(|| {
|
||||
O.call_once(|| {});
|
||||
});
|
||||
assert!(t.is_err());
|
||||
|
||||
// we can subvert poisoning, however
|
||||
let mut called = false;
|
||||
O.call_once_force(|p| {
|
||||
called = true;
|
||||
assert!(p.poisoned())
|
||||
});
|
||||
assert!(called);
|
||||
|
||||
// once any success happens, we stop propagating the poison
|
||||
O.call_once(|| {});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wait_for_force_to_finish() {
|
||||
static O: Once = Once::new();
|
||||
|
||||
// poison the once
|
||||
let t = panic::catch_unwind(|| {
|
||||
O.call_once(|| panic!());
|
||||
});
|
||||
assert!(t.is_err());
|
||||
|
||||
// make sure someone's waiting inside the once via a force
|
||||
let (tx1, rx1) = channel();
|
||||
let (tx2, rx2) = channel();
|
||||
let t1 = thread::spawn(move || {
|
||||
O.call_once_force(|p| {
|
||||
assert!(p.poisoned());
|
||||
tx1.send(()).unwrap();
|
||||
rx2.recv().unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
rx1.recv().unwrap();
|
||||
|
||||
// put another waiter on the once
|
||||
let t2 = thread::spawn(|| {
|
||||
let mut called = false;
|
||||
O.call_once(|| {
|
||||
called = true;
|
||||
});
|
||||
assert!(!called);
|
||||
});
|
||||
|
||||
tx2.send(()).unwrap();
|
||||
|
||||
assert!(t1.join().is_ok());
|
||||
assert!(t2.join().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_once_debug() {
|
||||
static O: Once = Once::new();
|
||||
|
||||
assert_eq!(format!("{:?}", O), "Once { state: New }");
|
||||
}
|
||||
}
|
||||
65
third-party/vendor/parking_lot/src/raw_fair_mutex.rs
vendored
Normal file
65
third-party/vendor/parking_lot/src/raw_fair_mutex.rs
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use crate::raw_mutex::RawMutex;
|
||||
use lock_api::RawMutexFair;
|
||||
|
||||
/// Raw fair mutex type backed by the parking lot.
|
||||
pub struct RawFairMutex(RawMutex);
|
||||
|
||||
unsafe impl lock_api::RawMutex for RawFairMutex {
|
||||
const INIT: Self = RawFairMutex(<RawMutex as lock_api::RawMutex>::INIT);
|
||||
|
||||
type GuardMarker = <RawMutex as lock_api::RawMutex>::GuardMarker;
|
||||
|
||||
#[inline]
|
||||
fn lock(&self) {
|
||||
self.0.lock()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_lock(&self) -> bool {
|
||||
self.0.try_lock()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn unlock(&self) {
|
||||
self.unlock_fair()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_locked(&self) -> bool {
|
||||
self.0.is_locked()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl lock_api::RawMutexFair for RawFairMutex {
|
||||
#[inline]
|
||||
unsafe fn unlock_fair(&self) {
|
||||
self.0.unlock_fair()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn bump(&self) {
|
||||
self.0.bump()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl lock_api::RawMutexTimed for RawFairMutex {
|
||||
type Duration = <RawMutex as lock_api::RawMutexTimed>::Duration;
|
||||
type Instant = <RawMutex as lock_api::RawMutexTimed>::Instant;
|
||||
|
||||
#[inline]
|
||||
fn try_lock_until(&self, timeout: Self::Instant) -> bool {
|
||||
self.0.try_lock_until(timeout)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_lock_for(&self, timeout: Self::Duration) -> bool {
|
||||
self.0.try_lock_for(timeout)
|
||||
}
|
||||
}
|
||||
331
third-party/vendor/parking_lot/src/raw_mutex.rs
vendored
Normal file
331
third-party/vendor/parking_lot/src/raw_mutex.rs
vendored
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use crate::{deadlock, util};
|
||||
use core::{
|
||||
sync::atomic::{AtomicU8, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
use lock_api::RawMutex as RawMutex_;
|
||||
use parking_lot_core::{self, ParkResult, SpinWait, UnparkResult, UnparkToken, DEFAULT_PARK_TOKEN};
|
||||
use std::time::Instant;
|
||||
|
||||
// UnparkToken used to indicate that that the target thread should attempt to
|
||||
// lock the mutex again as soon as it is unparked.
|
||||
pub(crate) const TOKEN_NORMAL: UnparkToken = UnparkToken(0);
|
||||
|
||||
// UnparkToken used to indicate that the mutex is being handed off to the target
|
||||
// thread directly without unlocking it.
|
||||
pub(crate) const TOKEN_HANDOFF: UnparkToken = UnparkToken(1);
|
||||
|
||||
/// This bit is set in the `state` of a `RawMutex` when that mutex is locked by some thread.
|
||||
const LOCKED_BIT: u8 = 0b01;
|
||||
/// This bit is set in the `state` of a `RawMutex` just before parking a thread. A thread is being
|
||||
/// parked if it wants to lock the mutex, but it is currently being held by some other thread.
|
||||
const PARKED_BIT: u8 = 0b10;
|
||||
|
||||
/// Raw mutex type backed by the parking lot.
|
||||
pub struct RawMutex {
|
||||
/// This atomic integer holds the current state of the mutex instance. Only the two lowest bits
|
||||
/// are used. See `LOCKED_BIT` and `PARKED_BIT` for the bitmask for these bits.
|
||||
///
|
||||
/// # State table:
|
||||
///
|
||||
/// PARKED_BIT | LOCKED_BIT | Description
|
||||
/// 0 | 0 | The mutex is not locked, nor is anyone waiting for it.
|
||||
/// -----------+------------+------------------------------------------------------------------
|
||||
/// 0 | 1 | The mutex is locked by exactly one thread. No other thread is
|
||||
/// | | waiting for it.
|
||||
/// -----------+------------+------------------------------------------------------------------
|
||||
/// 1 | 0 | The mutex is not locked. One or more thread is parked or about to
|
||||
/// | | park. At least one of the parked threads are just about to be
|
||||
/// | | unparked, or a thread heading for parking might abort the park.
|
||||
/// -----------+------------+------------------------------------------------------------------
|
||||
/// 1 | 1 | The mutex is locked by exactly one thread. One or more thread is
|
||||
/// | | parked or about to park, waiting for the lock to become available.
|
||||
/// | | In this state, PARKED_BIT is only ever cleared when a bucket lock
|
||||
/// | | is held (i.e. in a parking_lot_core callback). This ensures that
|
||||
/// | | we never end up in a situation where there are parked threads but
|
||||
/// | | PARKED_BIT is not set (which would result in those threads
|
||||
/// | | potentially never getting woken up).
|
||||
state: AtomicU8,
|
||||
}
|
||||
|
||||
unsafe impl lock_api::RawMutex for RawMutex {
|
||||
const INIT: RawMutex = RawMutex {
|
||||
state: AtomicU8::new(0),
|
||||
};
|
||||
|
||||
type GuardMarker = crate::GuardMarker;
|
||||
|
||||
#[inline]
|
||||
fn lock(&self) {
|
||||
if self
|
||||
.state
|
||||
.compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
self.lock_slow(None);
|
||||
}
|
||||
unsafe { deadlock::acquire_resource(self as *const _ as usize) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_lock(&self) -> bool {
|
||||
let mut state = self.state.load(Ordering::Relaxed);
|
||||
loop {
|
||||
if state & LOCKED_BIT != 0 {
|
||||
return false;
|
||||
}
|
||||
match self.state.compare_exchange_weak(
|
||||
state,
|
||||
state | LOCKED_BIT,
|
||||
Ordering::Acquire,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => {
|
||||
unsafe { deadlock::acquire_resource(self as *const _ as usize) };
|
||||
return true;
|
||||
}
|
||||
Err(x) => state = x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn unlock(&self) {
|
||||
deadlock::release_resource(self as *const _ as usize);
|
||||
if self
|
||||
.state
|
||||
.compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.unlock_slow(false);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_locked(&self) -> bool {
|
||||
let state = self.state.load(Ordering::Relaxed);
|
||||
state & LOCKED_BIT != 0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl lock_api::RawMutexFair for RawMutex {
|
||||
#[inline]
|
||||
unsafe fn unlock_fair(&self) {
|
||||
deadlock::release_resource(self as *const _ as usize);
|
||||
if self
|
||||
.state
|
||||
.compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.unlock_slow(true);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn bump(&self) {
|
||||
if self.state.load(Ordering::Relaxed) & PARKED_BIT != 0 {
|
||||
self.bump_slow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl lock_api::RawMutexTimed for RawMutex {
|
||||
type Duration = Duration;
|
||||
type Instant = Instant;
|
||||
|
||||
#[inline]
|
||||
fn try_lock_until(&self, timeout: Instant) -> bool {
|
||||
let result = if self
|
||||
.state
|
||||
.compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
self.lock_slow(Some(timeout))
|
||||
};
|
||||
if result {
|
||||
unsafe { deadlock::acquire_resource(self as *const _ as usize) };
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_lock_for(&self, timeout: Duration) -> bool {
|
||||
let result = if self
|
||||
.state
|
||||
.compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
self.lock_slow(util::to_deadline(timeout))
|
||||
};
|
||||
if result {
|
||||
unsafe { deadlock::acquire_resource(self as *const _ as usize) };
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl RawMutex {
|
||||
// Used by Condvar when requeuing threads to us, must be called while
|
||||
// holding the queue lock.
|
||||
#[inline]
|
||||
pub(crate) fn mark_parked_if_locked(&self) -> bool {
|
||||
let mut state = self.state.load(Ordering::Relaxed);
|
||||
loop {
|
||||
if state & LOCKED_BIT == 0 {
|
||||
return false;
|
||||
}
|
||||
match self.state.compare_exchange_weak(
|
||||
state,
|
||||
state | PARKED_BIT,
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => return true,
|
||||
Err(x) => state = x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used by Condvar when requeuing threads to us, must be called while
|
||||
// holding the queue lock.
|
||||
#[inline]
|
||||
pub(crate) fn mark_parked(&self) {
|
||||
self.state.fetch_or(PARKED_BIT, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn lock_slow(&self, timeout: Option<Instant>) -> bool {
|
||||
let mut spinwait = SpinWait::new();
|
||||
let mut state = self.state.load(Ordering::Relaxed);
|
||||
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,
|
||||
) {
|
||||
Ok(_) => return true,
|
||||
Err(x) => state = x,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there is no queue, try spinning a few times
|
||||
if state & PARKED_BIT == 0 && spinwait.spin() {
|
||||
state = self.state.load(Ordering::Relaxed);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the parked bit
|
||||
if state & PARKED_BIT == 0 {
|
||||
if let Err(x) = self.state.compare_exchange_weak(
|
||||
state,
|
||||
state | PARKED_BIT,
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
state = x;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Park our thread until we are woken up by an unlock
|
||||
let addr = self as *const _ as usize;
|
||||
let validate = || self.state.load(Ordering::Relaxed) == LOCKED_BIT | PARKED_BIT;
|
||||
let before_sleep = || {};
|
||||
let timed_out = |_, was_last_thread| {
|
||||
// Clear the parked bit if we were the last parked thread
|
||||
if was_last_thread {
|
||||
self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed);
|
||||
}
|
||||
};
|
||||
// SAFETY:
|
||||
// * `addr` is an address we control.
|
||||
// * `validate`/`timed_out` does not panic or call into any function of `parking_lot`.
|
||||
// * `before_sleep` does not call `park`, nor does it panic.
|
||||
match unsafe {
|
||||
parking_lot_core::park(
|
||||
addr,
|
||||
validate,
|
||||
before_sleep,
|
||||
timed_out,
|
||||
DEFAULT_PARK_TOKEN,
|
||||
timeout,
|
||||
)
|
||||
} {
|
||||
// The thread that unparked us passed the lock on to us
|
||||
// directly without unlocking it.
|
||||
ParkResult::Unparked(TOKEN_HANDOFF) => return true,
|
||||
|
||||
// We were unparked normally, try acquiring the lock again
|
||||
ParkResult::Unparked(_) => (),
|
||||
|
||||
// The validation function failed, try locking again
|
||||
ParkResult::Invalid => (),
|
||||
|
||||
// Timeout expired
|
||||
ParkResult::TimedOut => return false,
|
||||
}
|
||||
|
||||
// Loop back and try locking again
|
||||
spinwait.reset();
|
||||
state = self.state.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn unlock_slow(&self, force_fair: bool) {
|
||||
// Unpark one thread and leave the parked bit set if there might
|
||||
// still be parked threads on this address.
|
||||
let addr = self as *const _ as usize;
|
||||
let callback = |result: UnparkResult| {
|
||||
// If we are using a fair unlock then we should keep the
|
||||
// mutex locked and hand it off to the unparked thread.
|
||||
if result.unparked_threads != 0 && (force_fair || result.be_fair) {
|
||||
// Clear the parked bit if there are no more parked
|
||||
// threads.
|
||||
if !result.have_more_threads {
|
||||
self.state.store(LOCKED_BIT, Ordering::Relaxed);
|
||||
}
|
||||
return TOKEN_HANDOFF;
|
||||
}
|
||||
|
||||
// Clear the locked bit, and the parked bit as well if there
|
||||
// are no more parked threads.
|
||||
if result.have_more_threads {
|
||||
self.state.store(PARKED_BIT, Ordering::Release);
|
||||
} else {
|
||||
self.state.store(0, Ordering::Release);
|
||||
}
|
||||
TOKEN_NORMAL
|
||||
};
|
||||
// SAFETY:
|
||||
// * `addr` is an address we control.
|
||||
// * `callback` does not panic or call into any function of `parking_lot`.
|
||||
unsafe {
|
||||
parking_lot_core::unpark_one(addr, callback);
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn bump_slow(&self) {
|
||||
unsafe { deadlock::release_resource(self as *const _ as usize) };
|
||||
self.unlock_slow(true);
|
||||
self.lock();
|
||||
}
|
||||
}
|
||||
1149
third-party/vendor/parking_lot/src/raw_rwlock.rs
vendored
Normal file
1149
third-party/vendor/parking_lot/src/raw_rwlock.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
149
third-party/vendor/parking_lot/src/remutex.rs
vendored
Normal file
149
third-party/vendor/parking_lot/src/remutex.rs
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use crate::raw_mutex::RawMutex;
|
||||
use core::num::NonZeroUsize;
|
||||
use lock_api::{self, GetThreadId};
|
||||
|
||||
/// Implementation of the `GetThreadId` trait for `lock_api::ReentrantMutex`.
|
||||
pub struct RawThreadId;
|
||||
|
||||
unsafe impl GetThreadId for RawThreadId {
|
||||
const INIT: RawThreadId = RawThreadId;
|
||||
|
||||
fn nonzero_thread_id(&self) -> NonZeroUsize {
|
||||
// The address of a thread-local variable is guaranteed to be unique to the
|
||||
// current thread, and is also guaranteed to be non-zero. The variable has to have a
|
||||
// non-zero size to guarantee it has a unique address for each thread.
|
||||
thread_local!(static KEY: u8 = 0);
|
||||
KEY.with(|x| {
|
||||
NonZeroUsize::new(x as *const _ as usize)
|
||||
.expect("thread-local variable address is null")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A mutex which can be recursively locked by a single thread.
|
||||
///
|
||||
/// This type is identical to `Mutex` except for the following points:
|
||||
///
|
||||
/// - Locking multiple times from the same thread will work correctly instead of
|
||||
/// deadlocking.
|
||||
/// - `ReentrantMutexGuard` does not give mutable references to the locked data.
|
||||
/// Use a `RefCell` if you need this.
|
||||
///
|
||||
/// See [`Mutex`](type.Mutex.html) for more details about the underlying mutex
|
||||
/// primitive.
|
||||
pub type ReentrantMutex<T> = lock_api::ReentrantMutex<RawMutex, RawThreadId, T>;
|
||||
|
||||
/// Creates a new reentrant mutex in an unlocked state ready for use.
|
||||
///
|
||||
/// This allows creating a reentrant mutex in a constant context on stable Rust.
|
||||
pub const fn const_reentrant_mutex<T>(val: T) -> ReentrantMutex<T> {
|
||||
ReentrantMutex::const_new(
|
||||
<RawMutex as lock_api::RawMutex>::INIT,
|
||||
<RawThreadId as lock_api::GetThreadId>::INIT,
|
||||
val,
|
||||
)
|
||||
}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a reentrant mutex. When this structure
|
||||
/// is dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// `Deref` implementation.
|
||||
pub type ReentrantMutexGuard<'a, T> = lock_api::ReentrantMutexGuard<'a, RawMutex, RawThreadId, T>;
|
||||
|
||||
/// An RAII mutex guard returned by `ReentrantMutexGuard::map`, which can point to a
|
||||
/// subfield of the protected data.
|
||||
///
|
||||
/// The main difference between `MappedReentrantMutexGuard` and `ReentrantMutexGuard` is that the
|
||||
/// former doesn't support temporarily unlocking and re-locking, since that
|
||||
/// could introduce soundness issues if the locked object is modified by another
|
||||
/// thread.
|
||||
pub type MappedReentrantMutexGuard<'a, T> =
|
||||
lock_api::MappedReentrantMutexGuard<'a, RawMutex, RawThreadId, T>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ReentrantMutex;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let m = ReentrantMutex::new(2);
|
||||
{
|
||||
let a = m.lock();
|
||||
{
|
||||
let b = m.lock();
|
||||
{
|
||||
let c = m.lock();
|
||||
assert_eq!(*c, 2);
|
||||
}
|
||||
assert_eq!(*b, 2);
|
||||
}
|
||||
assert_eq!(*a, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_mutex() {
|
||||
let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
|
||||
let m2 = m.clone();
|
||||
let lock = m.lock();
|
||||
let child = thread::spawn(move || {
|
||||
let lock = m2.lock();
|
||||
assert_eq!(*lock.borrow(), 4950);
|
||||
});
|
||||
for i in 0..100 {
|
||||
let lock = m.lock();
|
||||
*lock.borrow_mut() += i;
|
||||
}
|
||||
drop(lock);
|
||||
child.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trylock_works() {
|
||||
let m = Arc::new(ReentrantMutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let _lock = m.try_lock();
|
||||
let _lock2 = m.try_lock();
|
||||
thread::spawn(move || {
|
||||
let lock = m2.try_lock();
|
||||
assert!(lock.is_none());
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
let _lock3 = m.try_lock();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reentrant_mutex_debug() {
|
||||
let mutex = ReentrantMutex::new(vec![0u8, 10]);
|
||||
|
||||
assert_eq!(format!("{:?}", mutex), "ReentrantMutex { data: [0, 10] }");
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn test_serde() {
|
||||
let contents: Vec<u8> = vec![0, 1, 2];
|
||||
let mutex = ReentrantMutex::new(contents.clone());
|
||||
|
||||
let serialized = serialize(&mutex).unwrap();
|
||||
let deserialized: ReentrantMutex<Vec<u8>> = deserialize(&serialized).unwrap();
|
||||
|
||||
assert_eq!(*(mutex.lock()), *(deserialized.lock()));
|
||||
assert_eq!(contents, *(deserialized.lock()));
|
||||
}
|
||||
}
|
||||
642
third-party/vendor/parking_lot/src/rwlock.rs
vendored
Normal file
642
third-party/vendor/parking_lot/src/rwlock.rs
vendored
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use crate::raw_rwlock::RawRwLock;
|
||||
use lock_api;
|
||||
|
||||
/// A reader-writer lock
|
||||
///
|
||||
/// This type of lock allows a number of readers or at most one writer at any
|
||||
/// point in time. The write portion of this lock typically allows modification
|
||||
/// of the underlying data (exclusive access) and the read portion of this lock
|
||||
/// typically allows for read-only access (shared access).
|
||||
///
|
||||
/// This lock uses a task-fair locking policy which avoids both reader and
|
||||
/// writer starvation. This means that readers trying to acquire the lock will
|
||||
/// block even if the lock is unlocked when there are writers waiting to acquire
|
||||
/// the lock. Because of this, attempts to recursively acquire a read lock
|
||||
/// within a single thread may result in a deadlock.
|
||||
///
|
||||
/// The type parameter `T` represents the data that this lock protects. It is
|
||||
/// required that `T` satisfies `Send` to be shared across threads and `Sync` to
|
||||
/// allow concurrent access through readers. The RAII guards returned from the
|
||||
/// locking methods implement `Deref` (and `DerefMut` for the `write` methods)
|
||||
/// to allow access to the contained of the lock.
|
||||
///
|
||||
/// # Fairness
|
||||
///
|
||||
/// A typical unfair lock can often end up in a situation where a single thread
|
||||
/// quickly acquires and releases the same lock in succession, which can starve
|
||||
/// other threads waiting to acquire the rwlock. While this improves throughput
|
||||
/// because it doesn't force a context switch when a thread tries to re-acquire
|
||||
/// a rwlock it has just released, this can starve other threads.
|
||||
///
|
||||
/// This rwlock uses [eventual fairness](https://trac.webkit.org/changeset/203350)
|
||||
/// to ensure that the lock will be fair on average without sacrificing
|
||||
/// throughput. This is done by forcing a fair unlock on average every 0.5ms,
|
||||
/// which will force the lock to go to the next thread waiting for the rwlock.
|
||||
///
|
||||
/// Additionally, any critical section longer than 1ms will always use a fair
|
||||
/// unlock, which has a negligible impact on throughput considering the length
|
||||
/// of the critical section.
|
||||
///
|
||||
/// You can also force a fair unlock by calling `RwLockReadGuard::unlock_fair`
|
||||
/// or `RwLockWriteGuard::unlock_fair` when unlocking a mutex instead of simply
|
||||
/// dropping the guard.
|
||||
///
|
||||
/// # Differences from the standard library `RwLock`
|
||||
///
|
||||
/// - Supports atomically downgrading a write lock into a read lock.
|
||||
/// - Task-fair locking policy instead of an unspecified platform default.
|
||||
/// - No poisoning, the lock is released normally on panic.
|
||||
/// - Only requires 1 word of space, whereas the standard library boxes the
|
||||
/// `RwLock` due to platform limitations.
|
||||
/// - Can be statically constructed.
|
||||
/// - Does not require any drop glue when dropped.
|
||||
/// - Inline fast path for the uncontended case.
|
||||
/// - Efficient handling of micro-contention using adaptive spinning.
|
||||
/// - Allows raw locking & unlocking without a guard.
|
||||
/// - Supports eventual fairness so that the rwlock is fair on average.
|
||||
/// - Optionally allows making the rwlock fair by calling
|
||||
/// `RwLockReadGuard::unlock_fair` and `RwLockWriteGuard::unlock_fair`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use parking_lot::RwLock;
|
||||
///
|
||||
/// let lock = RwLock::new(5);
|
||||
///
|
||||
/// // many reader locks can be held at once
|
||||
/// {
|
||||
/// let r1 = lock.read();
|
||||
/// let r2 = lock.read();
|
||||
/// assert_eq!(*r1, 5);
|
||||
/// assert_eq!(*r2, 5);
|
||||
/// } // read locks are dropped at this point
|
||||
///
|
||||
/// // only one write lock may be held, however
|
||||
/// {
|
||||
/// let mut w = lock.write();
|
||||
/// *w += 1;
|
||||
/// assert_eq!(*w, 6);
|
||||
/// } // write lock is dropped here
|
||||
/// ```
|
||||
pub type RwLock<T> = lock_api::RwLock<RawRwLock, T>;
|
||||
|
||||
/// Creates a new instance of an `RwLock<T>` which is unlocked.
|
||||
///
|
||||
/// This allows creating a `RwLock<T>` in a constant context on stable Rust.
|
||||
pub const fn const_rwlock<T>(val: T) -> RwLock<T> {
|
||||
RwLock::const_new(<RawRwLock as lock_api::RawRwLock>::INIT, val)
|
||||
}
|
||||
|
||||
/// RAII structure used to release the shared read access of a lock when
|
||||
/// dropped.
|
||||
pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>;
|
||||
|
||||
/// RAII structure used to release the exclusive write access of a lock when
|
||||
/// dropped.
|
||||
pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>;
|
||||
|
||||
/// An RAII read lock guard returned by `RwLockReadGuard::map`, which can point to a
|
||||
/// subfield of the protected data.
|
||||
///
|
||||
/// The main difference between `MappedRwLockReadGuard` and `RwLockReadGuard` is that the
|
||||
/// former doesn't support temporarily unlocking and re-locking, since that
|
||||
/// could introduce soundness issues if the locked object is modified by another
|
||||
/// thread.
|
||||
pub type MappedRwLockReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, RawRwLock, T>;
|
||||
|
||||
/// An RAII write lock guard returned by `RwLockWriteGuard::map`, which can point to a
|
||||
/// subfield of the protected data.
|
||||
///
|
||||
/// The main difference between `MappedRwLockWriteGuard` and `RwLockWriteGuard` is that the
|
||||
/// former doesn't support temporarily unlocking and re-locking, since that
|
||||
/// could introduce soundness issues if the locked object is modified by another
|
||||
/// thread.
|
||||
pub type MappedRwLockWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, RawRwLock, T>;
|
||||
|
||||
/// RAII structure used to release the upgradable read access of a lock when
|
||||
/// dropped.
|
||||
pub type RwLockUpgradableReadGuard<'a, T> = lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard};
|
||||
use rand::Rng;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopy(i32);
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let l = RwLock::new(());
|
||||
drop(l.read());
|
||||
drop(l.write());
|
||||
drop(l.upgradable_read());
|
||||
drop((l.read(), l.read()));
|
||||
drop((l.read(), l.upgradable_read()));
|
||||
drop(l.write());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frob() {
|
||||
const N: u32 = 10;
|
||||
const M: u32 = 1000;
|
||||
|
||||
let r = Arc::new(RwLock::new(()));
|
||||
|
||||
let (tx, rx) = channel::<()>();
|
||||
for _ in 0..N {
|
||||
let tx = tx.clone();
|
||||
let r = r.clone();
|
||||
thread::spawn(move || {
|
||||
let mut rng = rand::thread_rng();
|
||||
for _ in 0..M {
|
||||
if rng.gen_bool(1.0 / N as f64) {
|
||||
drop(r.write());
|
||||
} else {
|
||||
drop(r.read());
|
||||
}
|
||||
}
|
||||
drop(tx);
|
||||
});
|
||||
}
|
||||
drop(tx);
|
||||
let _ = rx.recv();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_arc_no_poison_wr() {
|
||||
let arc = Arc::new(RwLock::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let _: Result<(), _> = thread::spawn(move || {
|
||||
let _lock = arc2.write();
|
||||
panic!();
|
||||
})
|
||||
.join();
|
||||
let lock = arc.read();
|
||||
assert_eq!(*lock, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_arc_no_poison_ww() {
|
||||
let arc = Arc::new(RwLock::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let _: Result<(), _> = thread::spawn(move || {
|
||||
let _lock = arc2.write();
|
||||
panic!();
|
||||
})
|
||||
.join();
|
||||
let lock = arc.write();
|
||||
assert_eq!(*lock, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_arc_no_poison_rr() {
|
||||
let arc = Arc::new(RwLock::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let _: Result<(), _> = thread::spawn(move || {
|
||||
let _lock = arc2.read();
|
||||
panic!();
|
||||
})
|
||||
.join();
|
||||
let lock = arc.read();
|
||||
assert_eq!(*lock, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_arc_no_poison_rw() {
|
||||
let arc = Arc::new(RwLock::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let _: Result<(), _> = thread::spawn(move || {
|
||||
let _lock = arc2.read();
|
||||
panic!()
|
||||
})
|
||||
.join();
|
||||
let lock = arc.write();
|
||||
assert_eq!(*lock, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ruw_arc() {
|
||||
let arc = Arc::new(RwLock::new(0));
|
||||
let arc2 = arc.clone();
|
||||
let (tx, rx) = channel();
|
||||
|
||||
thread::spawn(move || {
|
||||
for _ in 0..10 {
|
||||
let mut lock = arc2.write();
|
||||
let tmp = *lock;
|
||||
*lock = -1;
|
||||
thread::yield_now();
|
||||
*lock = tmp + 1;
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
let mut children = Vec::new();
|
||||
|
||||
// Upgradable readers try to catch the writer in the act and also
|
||||
// try to touch the value
|
||||
for _ in 0..5 {
|
||||
let arc3 = arc.clone();
|
||||
children.push(thread::spawn(move || {
|
||||
let lock = arc3.upgradable_read();
|
||||
let tmp = *lock;
|
||||
assert!(tmp >= 0);
|
||||
thread::yield_now();
|
||||
let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
|
||||
assert_eq!(tmp, *lock);
|
||||
*lock = -1;
|
||||
thread::yield_now();
|
||||
*lock = tmp + 1;
|
||||
}));
|
||||
}
|
||||
|
||||
// Readers try to catch the writers in the act
|
||||
for _ in 0..5 {
|
||||
let arc4 = arc.clone();
|
||||
children.push(thread::spawn(move || {
|
||||
let lock = arc4.read();
|
||||
assert!(*lock >= 0);
|
||||
}));
|
||||
}
|
||||
|
||||
// Wait for children to pass their asserts
|
||||
for r in children {
|
||||
assert!(r.join().is_ok());
|
||||
}
|
||||
|
||||
// Wait for writer to finish
|
||||
rx.recv().unwrap();
|
||||
let lock = arc.read();
|
||||
assert_eq!(*lock, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_arc() {
|
||||
let arc = Arc::new(RwLock::new(0));
|
||||
let arc2 = arc.clone();
|
||||
let (tx, rx) = channel();
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut lock = arc2.write();
|
||||
for _ in 0..10 {
|
||||
let tmp = *lock;
|
||||
*lock = -1;
|
||||
thread::yield_now();
|
||||
*lock = tmp + 1;
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
// Readers try to catch the writer in the act
|
||||
let mut children = Vec::new();
|
||||
for _ in 0..5 {
|
||||
let arc3 = arc.clone();
|
||||
children.push(thread::spawn(move || {
|
||||
let lock = arc3.read();
|
||||
assert!(*lock >= 0);
|
||||
}));
|
||||
}
|
||||
|
||||
// Wait for children to pass their asserts
|
||||
for r in children {
|
||||
assert!(r.join().is_ok());
|
||||
}
|
||||
|
||||
// Wait for writer to finish
|
||||
rx.recv().unwrap();
|
||||
let lock = arc.read();
|
||||
assert_eq!(*lock, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_arc_access_in_unwind() {
|
||||
let arc = Arc::new(RwLock::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
struct Unwinder {
|
||||
i: Arc<RwLock<isize>>,
|
||||
}
|
||||
impl Drop for Unwinder {
|
||||
fn drop(&mut self) {
|
||||
let mut lock = self.i.write();
|
||||
*lock += 1;
|
||||
}
|
||||
}
|
||||
let _u = Unwinder { i: arc2 };
|
||||
panic!();
|
||||
})
|
||||
.join();
|
||||
let lock = arc.read();
|
||||
assert_eq!(*lock, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_unsized() {
|
||||
let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]);
|
||||
{
|
||||
let b = &mut *rw.write();
|
||||
b[0] = 4;
|
||||
b[2] = 5;
|
||||
}
|
||||
let comp: &[i32] = &[4, 2, 5];
|
||||
assert_eq!(&*rw.read(), comp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_try_read() {
|
||||
let lock = RwLock::new(0isize);
|
||||
{
|
||||
let read_guard = lock.read();
|
||||
|
||||
let read_result = lock.try_read();
|
||||
assert!(
|
||||
read_result.is_some(),
|
||||
"try_read should succeed while read_guard is in scope"
|
||||
);
|
||||
|
||||
drop(read_guard);
|
||||
}
|
||||
{
|
||||
let upgrade_guard = lock.upgradable_read();
|
||||
|
||||
let read_result = lock.try_read();
|
||||
assert!(
|
||||
read_result.is_some(),
|
||||
"try_read should succeed while upgrade_guard is in scope"
|
||||
);
|
||||
|
||||
drop(upgrade_guard);
|
||||
}
|
||||
{
|
||||
let write_guard = lock.write();
|
||||
|
||||
let read_result = lock.try_read();
|
||||
assert!(
|
||||
read_result.is_none(),
|
||||
"try_read should fail while write_guard is in scope"
|
||||
);
|
||||
|
||||
drop(write_guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_try_write() {
|
||||
let lock = RwLock::new(0isize);
|
||||
{
|
||||
let read_guard = lock.read();
|
||||
|
||||
let write_result = lock.try_write();
|
||||
assert!(
|
||||
write_result.is_none(),
|
||||
"try_write should fail while read_guard is in scope"
|
||||
);
|
||||
assert!(lock.is_locked());
|
||||
assert!(!lock.is_locked_exclusive());
|
||||
|
||||
drop(read_guard);
|
||||
}
|
||||
{
|
||||
let upgrade_guard = lock.upgradable_read();
|
||||
|
||||
let write_result = lock.try_write();
|
||||
assert!(
|
||||
write_result.is_none(),
|
||||
"try_write should fail while upgrade_guard is in scope"
|
||||
);
|
||||
assert!(lock.is_locked());
|
||||
assert!(!lock.is_locked_exclusive());
|
||||
|
||||
drop(upgrade_guard);
|
||||
}
|
||||
{
|
||||
let write_guard = lock.write();
|
||||
|
||||
let write_result = lock.try_write();
|
||||
assert!(
|
||||
write_result.is_none(),
|
||||
"try_write should fail while write_guard is in scope"
|
||||
);
|
||||
assert!(lock.is_locked());
|
||||
assert!(lock.is_locked_exclusive());
|
||||
|
||||
drop(write_guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_try_upgrade() {
|
||||
let lock = RwLock::new(0isize);
|
||||
{
|
||||
let read_guard = lock.read();
|
||||
|
||||
let upgrade_result = lock.try_upgradable_read();
|
||||
assert!(
|
||||
upgrade_result.is_some(),
|
||||
"try_upgradable_read should succeed while read_guard is in scope"
|
||||
);
|
||||
|
||||
drop(read_guard);
|
||||
}
|
||||
{
|
||||
let upgrade_guard = lock.upgradable_read();
|
||||
|
||||
let upgrade_result = lock.try_upgradable_read();
|
||||
assert!(
|
||||
upgrade_result.is_none(),
|
||||
"try_upgradable_read should fail while upgrade_guard is in scope"
|
||||
);
|
||||
|
||||
drop(upgrade_guard);
|
||||
}
|
||||
{
|
||||
let write_guard = lock.write();
|
||||
|
||||
let upgrade_result = lock.try_upgradable_read();
|
||||
assert!(
|
||||
upgrade_result.is_none(),
|
||||
"try_upgradable should fail while write_guard is in scope"
|
||||
);
|
||||
|
||||
drop(write_guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let m = RwLock::new(NonCopy(10));
|
||||
assert_eq!(m.into_inner(), NonCopy(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner_drop() {
|
||||
struct Foo(Arc<AtomicUsize>);
|
||||
impl Drop for Foo {
|
||||
fn drop(&mut self) {
|
||||
self.0.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
let num_drops = Arc::new(AtomicUsize::new(0));
|
||||
let m = RwLock::new(Foo(num_drops.clone()));
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
|
||||
{
|
||||
let _inner = m.into_inner();
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
|
||||
}
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut m = RwLock::new(NonCopy(10));
|
||||
*m.get_mut() = NonCopy(20);
|
||||
assert_eq!(m.into_inner(), NonCopy(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlockguard_sync() {
|
||||
fn sync<T: Sync>(_: T) {}
|
||||
|
||||
let rwlock = RwLock::new(());
|
||||
sync(rwlock.read());
|
||||
sync(rwlock.write());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_downgrade() {
|
||||
let x = Arc::new(RwLock::new(0));
|
||||
let mut handles = Vec::new();
|
||||
for _ in 0..8 {
|
||||
let x = x.clone();
|
||||
handles.push(thread::spawn(move || {
|
||||
for _ in 0..100 {
|
||||
let mut writer = x.write();
|
||||
*writer += 1;
|
||||
let cur_val = *writer;
|
||||
let reader = RwLockWriteGuard::downgrade(writer);
|
||||
assert_eq!(cur_val, *reader);
|
||||
}
|
||||
}));
|
||||
}
|
||||
for handle in handles {
|
||||
handle.join().unwrap()
|
||||
}
|
||||
assert_eq!(*x.read(), 800);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_recursive() {
|
||||
let arc = Arc::new(RwLock::new(1));
|
||||
let arc2 = arc.clone();
|
||||
let lock1 = arc.read();
|
||||
let t = thread::spawn(move || {
|
||||
let _lock = arc2.write();
|
||||
});
|
||||
|
||||
if cfg!(not(all(target_env = "sgx", target_vendor = "fortanix"))) {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
} else {
|
||||
// FIXME: https://github.com/fortanix/rust-sgx/issues/31
|
||||
for _ in 0..100 {
|
||||
thread::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
// A normal read would block here since there is a pending writer
|
||||
let lock2 = arc.read_recursive();
|
||||
|
||||
// Unblock the thread and join it.
|
||||
drop(lock1);
|
||||
drop(lock2);
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rwlock_debug() {
|
||||
let x = RwLock::new(vec![0u8, 10]);
|
||||
|
||||
assert_eq!(format!("{:?}", x), "RwLock { data: [0, 10] }");
|
||||
let _lock = x.write();
|
||||
assert_eq!(format!("{:?}", x), "RwLock { data: <locked> }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
let rwlock = RwLock::new(Arc::new(1));
|
||||
let a = rwlock.read_recursive();
|
||||
let b = a.clone();
|
||||
assert_eq!(Arc::strong_count(&b), 2);
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn test_serde() {
|
||||
let contents: Vec<u8> = vec![0, 1, 2];
|
||||
let mutex = RwLock::new(contents.clone());
|
||||
|
||||
let serialized = serialize(&mutex).unwrap();
|
||||
let deserialized: RwLock<Vec<u8>> = deserialize(&serialized).unwrap();
|
||||
|
||||
assert_eq!(*(mutex.read()), *(deserialized.read()));
|
||||
assert_eq!(contents, *(deserialized.read()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_issue_203() {
|
||||
struct Bar(RwLock<()>);
|
||||
|
||||
impl Drop for Bar {
|
||||
fn drop(&mut self) {
|
||||
let _n = self.0.write();
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static B: Bar = Bar(RwLock::new(()));
|
||||
}
|
||||
|
||||
thread::spawn(|| {
|
||||
B.with(|_| ());
|
||||
|
||||
let a = RwLock::new(());
|
||||
let _a = a.read();
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_write_is_locked() {
|
||||
let lock = RwLock::new(0isize);
|
||||
{
|
||||
let _read_guard = lock.read();
|
||||
|
||||
assert!(lock.is_locked());
|
||||
assert!(!lock.is_locked_exclusive());
|
||||
}
|
||||
|
||||
{
|
||||
let _write_guard = lock.write();
|
||||
|
||||
assert!(lock.is_locked());
|
||||
assert!(lock.is_locked_exclusive());
|
||||
}
|
||||
}
|
||||
}
|
||||
38
third-party/vendor/parking_lot/src/util.rs
vendored
Normal file
38
third-party/vendor/parking_lot/src/util.rs
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
// Option::unchecked_unwrap
|
||||
pub trait UncheckedOptionExt<T> {
|
||||
unsafe fn unchecked_unwrap(self) -> T;
|
||||
}
|
||||
|
||||
impl<T> UncheckedOptionExt<T> for Option<T> {
|
||||
#[inline]
|
||||
unsafe fn unchecked_unwrap(self) -> T {
|
||||
match self {
|
||||
Some(x) => x,
|
||||
None => unreachable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hint::unreachable_unchecked() in release mode
|
||||
#[inline]
|
||||
unsafe fn unreachable() -> ! {
|
||||
if cfg!(debug_assertions) {
|
||||
unreachable!();
|
||||
} else {
|
||||
core::hint::unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_deadline(timeout: Duration) -> Option<Instant> {
|
||||
Instant::now().checked_add(timeout)
|
||||
}
|
||||
26
third-party/vendor/parking_lot/tests/issue_203.rs
vendored
Normal file
26
third-party/vendor/parking_lot/tests/issue_203.rs
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use parking_lot::RwLock;
|
||||
use std::thread;
|
||||
|
||||
struct Bar(RwLock<()>);
|
||||
|
||||
impl Drop for Bar {
|
||||
fn drop(&mut self) {
|
||||
let _n = self.0.write();
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static B: Bar = Bar(RwLock::new(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn main() {
|
||||
thread::spawn(|| {
|
||||
B.with(|_| ());
|
||||
|
||||
let a = RwLock::new(());
|
||||
let _a = a.read();
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue